import { contentType } from "jsr:@std/media-types"; import { ModuleProxy } from "./hmr/hmr-static.tsx"; const keyBundle = encodeURI(">"); const keyAdjacent = encodeURI("^"); const keyReload = "reload"; const keysExtension = ["ts", "tsx"]; const extractExtension =(path:string)=> { const ind = path.lastIndexOf("."); return ind === -1 ? "" : path.substring(ind+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", format:"esm", write: false, minify: true, } const bakeConfigLocal:Deno.bundle.Options = {...bakeConfigPackage, minify:false, sourcemap:"inline", inlineImports:false }; async function BakeForce(path:string, type?:"package") { const config = type ? bakeConfigPackage : bakeConfigLocal; config.entrypoints[0] = type ? path : RootRunning+path; console.log("baking", config.entrypoints); 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 await BakeForce(path, type); } return lookup; } type CachedTranspile = [file:string, profile:string] const BakeCache:Record = {} const denoBody = await fetch(RootRunning+"/deno.json").then(resp=>resp.json()); for(const key in denoBody.imports) { const value = denoBody.imports[key]; if(value.startsWith("npm:")) { denoBody.imports[key] = "/>/"+value; } } denoBody.imports["react/jsx-runtime"] = "/>/hmr/hmr-react.tsx"; denoBody.imports["react-original"] = denoBody.imports[denoBody.compilerOptions.jsxImportSource]+"/jsx-runtime"; denoBody.imports["react"] = "/>/hmr/hmr-react.tsx"; // denoBody.imports["react-original"] = denoBody.imports["react"]; // denoBody.imports["react-jsx-runtime-original"] = denoBody.imports[denoBody.compilerOptions.jsxImportSource]+"/jsx-runtime"; // denoBody.imports["signals-original"] = denoBody.imports["@preact/signals"]; // denoBody.imports["@preact/signals"] = "/^/hmr/hmr-signal.tsx"; // denoBody.imports["react"] = "/^/hmr/hmr-react.tsx"; // denoBody.imports["react/jsx-runtime"] = "/^/hmr/hmr-react.tsx"; console.log(denoBody.imports); const importMap = ``; let htmlPageBody = await fetch(RootRunning+"/index.html").then(resp=>resp.text()); htmlPageBody = htmlPageBody.replace("", ""+importMap); const htmlPageHead = {headers:{"content-type":"text/html"}} const IndexResponse =()=> new Response(htmlPageBody, htmlPageHead); const JSHead = {headers:{"content-type":"application/javascript"}}; const JSResponse =(body:string)=>new Response(body, JSHead); Deno.serve(async(req:Request)=> { 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); } } const url = new URL(req.url); const parts = url.pathname.split("/").filter(part=>part); const lastPart = parts.at(-1); const extension = extractExtension(lastPart); if(parts[0] == keyBundle) { const proxiedPath = parts.slice(1).join("/"); const transpiled = await BakeCheck(proxiedPath, "package"); return JSResponse(transpiled[0]); } if(parts[0] == keyAdjacent) { const proxiedPath = "/"+parts.slice(1).join("/"); const transpiled = await BakeCheck(proxiedPath); return JSResponse(transpiled[0]); } if(keysExtension.includes(extension)) { const transpiled = await BakeCheck(url.pathname); return JSResponse(transpiled[url.searchParams.has(keyReload) ? 0 : 1]); } if(!extension) { return IndexResponse(); } 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)) { const key = path.substring(Deno.cwd().length).replaceAll("\\", "/"); console.log("File change", path, key); if(action != "remove") { await BakeForce(key); SocketsSend(key); } else { delete BakeCache[key]; } } } filesChanged.clear(); blocking = false; } , 1000); } } } Watcher();