// deno-fmt-ignore-file import { serveDir } from "https://deno.land/std@0.216.0/http/file_server.ts"; import { join } from "https://deno.land/std@0.216.0/path/join.ts"; // add reference to global cache? // https://docs.deno.com/runtime/manual/runtime/web_platform_apis#deviations-of-other-apis-from-spec type HttpFn = (request: Request, url: URL, match: URLPatternResult, headers: Headers) => Promise | Response; type HttpMethod = "GET" | "POST" | "PATCH" | "DELETE" | "PUT" | "HEAD" | "OPTIONS" | "CONNECT" | "TRACE"; type Route = [URLPattern, HttpMethod, string]; let GLOBAL_ROUTE_MODULE: { routes: Route[], headers: () => Headers }; let liveVersion: string; async function fetchGlobalRouteModule(): Promise { const polledVersion = (await Deno.readTextFile("./live.txt")).trim(); if (polledVersion !== liveVersion) { console.info(`changing version from ${liveVersion} to ${polledVersion}`) liveVersion = polledVersion; } const currentModule = await import(import.meta.dirname! + "/pkg/route/" + liveVersion + "/mod.ts"); GLOBAL_ROUTE_MODULE = currentModule; } setInterval(fetchGlobalRouteModule, 500); // Can cache be outside of the handler to ensure they reside? // Also - there is max a couple hundred pages, because they are individual .mjs files // NOT based on web content. Just the javascript, which is defined. // Data being piped through is either server side generated or api async function main(request: Request) { const url = new URL(request.url); for (const [pattern, method, httpFnModuleName] of GLOBAL_ROUTE_MODULE.routes) { const match: URLPatternResult | null = pattern.exec(url); if (match === null) continue; if (request.method === method) { // this needs to go to pkg.example.internal // and when it says internal, it means internal // any assets served are via a route - files must be proxied out if they are as-is // which is somewhat rare that they don't need transpilation // but what about pkg.example.com (pkg.localhost), is that the public part of pkg.example.internal, but bundled and/or explicit? // how and why use `pkg.` rather than a path? it is because of public assets referencing hard links in async imports and import maps; // they need separate host name. so pkg becomes a subhost? who/what is routable under that subhost? // is this a 'javascript virutal file system' because it requires a build system for ts/tsx/jsx? then pkg.localhost is not real. // it exists as a namespace to not conflict with top level package routes like "http://localhost/editor/0.0.0/mod.js" // and instead is http://localhost/editor/0.0.0/mod.js // what about ts files getting converted to js for client side? what do do about path identifier? // Do these files have .tsx but then write to client .js? // Or only js files to client? not even jsx? // Either the bundler behind .pkg presents the view that it is .js or it is .tsx - but then what of the browser? // And these paths need to keep the 1:1 file representation. Just how to link the two. // And LSP views .js as nothing but .js, right? or is this configurable? // Seemingly the only other way is to force .js only for client dev - which makes sense - but is out of overton window. // Also Tailwind is something else... does this happen once bundling is over for .mjs files only? or only TSX/JSX? // React DOM is a global execution context. and so is tailwind. how does this work client side then? const modulePath = join(import.meta.dirname! + "/pkg/" + httpFnModuleName); const module = await import(modulePath); const httpFn: HttpFn = module.default; return await httpFn(request, url, match, GLOBAL_ROUTE_MODULE.headers()); } } return serveDir(request, { fsRoot: "pkg", showDirListing: true }); } // Run once on startup await fetchGlobalRouteModule(); Deno.serve({ port: 80 }, main);