separation started, hmr broken

(bad path strings)
This commit is contained in:
Seth Trowbridge 2025-10-15 16:55:21 -04:00
parent 597aabe924
commit dfa3877013
3 changed files with 46 additions and 30 deletions

View File

@ -22,7 +22,9 @@ function connect() {
}); });
socket.addEventListener("message", async (ev) => { socket.addEventListener("message", async (ev) => {
try { try {
const reImport = await import(location.origin + ev.data + "?reload=" + Math.random()); const reloadPath = location.origin + ev.data + "?reload=" + Math.random();
console.log("realod called", ev.data, reloadPath);
const reImport = await import(reloadPath);
FileListeners.get(ev.data)?.forEach(h => h(reImport)); FileListeners.get(ev.data)?.forEach(h => h(reImport));
HMR.update(); HMR.update();
} catch (e) { } catch (e) {

View File

@ -114,11 +114,11 @@ export const ModuleShape =(inText:string)=>
return[local, foreign] as [local:string[], foreign:string[]]; return[local, foreign] as [local:string[], foreign:string[]];
}; };
export const ModuleProxy =(inText:string, inPath:string)=> export const ModuleProxy =(inText:string, inPath:string, reactProxy:string)=>
{ {
const [local, foreign] = ModuleShape(inText); const [local, foreign] = ModuleShape(inText);
return ` return `
import {FileListen} from "/^/hmr/hmr-listen.tsx"; import {FileListen} from "${reactProxy}";
import * as Import from "${inPath}?reload=${new Date().getTime()}"; import * as Import from "${inPath}?reload=${new Date().getTime()}";
${ local.map(m=>`let proxy_${m} = Import.${m}; export { proxy_${m} as ${m} };`).join("\n") } ${ local.map(m=>`let proxy_${m} = Import.${m}; export { proxy_${m} as ${m} };`).join("\n") }
FileListen("${inPath}", (updatedModule)=> FileListen("${inPath}", (updatedModule)=>

View File

@ -1,8 +1,8 @@
import { contentType } from "jsr:@std/media-types"; import { contentType } from "jsr:@std/media-types";
import { ModuleProxy } from "./hmr/hmr-static.tsx"; import { ModuleProxy } from "./hmr/hmr-static.tsx";
const keyBundle = encodeURI(">"); const keyBundle = "=>";
const keyAdjacent = encodeURI("^"); const keyTranspile = "->";
const keyReload = "reload"; const keyReload = "reload";
const keysExtension = ["ts", "tsx"]; const keysExtension = ["ts", "tsx"];
const extractExtension =(path:string)=> const extractExtension =(path:string)=>
@ -28,12 +28,17 @@ const bakeConfigPackage:Deno.bundle.Options =
const bakeConfigLocal:Deno.bundle.Options = {...bakeConfigPackage, sourcemap:"inline", inlineImports:false }; const bakeConfigLocal:Deno.bundle.Options = {...bakeConfigPackage, sourcemap:"inline", inlineImports:false };
type FullBakeConfig = { type FullBakeConfig = {
bundle: Deno.bundle.Options, path:string,
key?:string,
bundle?:boolean
}; };
async function BakeForce(prefix:string, path:string, key:string, type?:"package") async function BakeForce(options:FullBakeConfig)
{ {
const path = options.path;
const key = options.key || path;
const type = options.bundle;
// If already baking, return the in-flight promise. (Caller may also call Bake Check which handles this.) // If already baking, return the in-flight promise. (Caller may also call Bake Check which handles this.)
if (BakeCache[key] && typeof (BakeCache[key] as any)?.then === "function") if (BakeCache[key] && typeof (BakeCache[key] as any)?.then === "function")
{ {
@ -41,8 +46,8 @@ async function BakeForce(prefix:string, path:string, key:string, type?:"package"
} }
// Create a fresh config per bake to avoid shared mutation. // Create a fresh config per bake to avoid shared mutation.
const config = {...(type ? bakeConfigPackage : bakeConfigLocal), entrypoints:[prefix + path]}; const config = {...(type ? bakeConfigPackage : bakeConfigLocal), entrypoints:[path]};
console.log("baking", config.entrypoints, "as", [prefix, path]); console.log("baking", config.entrypoints, "as", key);
// store the in-flight promise immediately so concurrent callers reuse it // store the in-flight promise immediately so concurrent callers reuse it
const inflight = (async () => const inflight = (async () =>
@ -53,7 +58,8 @@ async function BakeForce(prefix:string, path:string, key:string, type?:"package"
if (result.outputFiles) if (result.outputFiles)
{ {
const body = result.outputFiles.map(file=>file.text()).join("\n"); const body = result.outputFiles.map(file=>file.text()).join("\n");
const save:CachedTranspile = [body, type ? "" : ModuleProxy(body, path)]; const listenerImport = `/${keyTranspile}/${import.meta.resolve("./hmr/hmr-listen.tsx")}`;
const save:CachedTranspile = [body, type ? "" : ModuleProxy(body, path, listenerImport)];
BakeCache[key] = save; // replace promise with resolved value BakeCache[key] = save; // replace promise with resolved value
return save; return save;
} }
@ -70,12 +76,14 @@ async function BakeForce(prefix:string, path:string, key:string, type?:"package"
BakeCache[key] = inflight; BakeCache[key] = inflight;
return await inflight; return await inflight;
}; };
async function BakeCheck(prefix:string, path:string, key:string, type?:"package") async function BakeCheck(options:FullBakeConfig)
{ {
const lookup = BakeCache[path]; const key = options.key || options.path;
const lookup = BakeCache[key];
if(!lookup) if(!lookup)
{ {
return await BakeForce(prefix, path, type); return await BakeForce(options);
} }
// if an in-flight promise is stored, await it // if an in-flight promise is stored, await it
if (typeof (lookup as any)?.then === "function") if (typeof (lookup as any)?.then === "function")
@ -94,13 +102,14 @@ for(const key in denoBody.imports)
const value = denoBody.imports[key]; const value = denoBody.imports[key];
if(value.startsWith("npm:")) if(value.startsWith("npm:"))
{ {
denoBody.imports[key] = "/>/"+value; denoBody.imports[key] = `/${keyBundle}/${value}`;
} }
} }
const react = denoBody.compilerOptions.jsxImportSource; const reactKey = denoBody.compilerOptions.jsxImportSource;
denoBody.imports["react-original"] = denoBody.imports[react]; denoBody.imports["react-original"] = denoBody.imports[reactKey];
denoBody.imports[react] = "/^/hmr/hmr-react.tsx"; const reactValueModified = `/${keyTranspile}/${import.meta.resolve("./hmr/hmr-react.tsx")}`;
denoBody.imports[react+"/jsx-runtime"] = "/^/hmr/hmr-react.tsx"; denoBody.imports[reactKey] = reactValueModified
denoBody.imports[reactKey+"/jsx-runtime"] = reactValueModified;
/* /*
@ -152,7 +161,7 @@ Deno.serve(async(req:Request)=>
} }
const url = new URL(req.url); const url = new URL(req.url);
const parts = url.pathname.split("/").filter(part=>part); const parts = url.pathname.split("/").filter(part=>part).map(part=>decodeURIComponent(part));
// if there are no path segments, serve index immediately (avoid calling extractExtension on undefined) // if there are no path segments, serve index immediately (avoid calling extractExtension on undefined)
if(parts.length === 0) if(parts.length === 0)
@ -164,20 +173,23 @@ Deno.serve(async(req:Request)=>
if(parts[0] == keyBundle) if(parts[0] == keyBundle)
{ {
const proxiedPath = parts.slice(1).join("/"); console.log("BUNDLE", parts);
const transpiled = await BakeCheck(proxiedPath, "package"); const transpiled = await BakeCheck({
path: parts.slice(1).join("/"),
bundle:true
});
return JSResponse(transpiled[0]); return JSResponse(transpiled[0]);
} }
if(parts[0] == keyAdjacent) if(parts[0] == keyTranspile)
{ {
const proxiedPath = RootSiblings + "/" + parts.slice(1).join("/"); console.log("TRANSPILE", parts);
const transpiled = await BakeCheck(proxiedPath); const transpiled = await BakeCheck({path:parts.slice(1).join("/"), bundle:false});
return JSResponse(transpiled[0]); return JSResponse(transpiled[0]);
} }
if(keysExtension.includes(extension)) if(keysExtension.includes(extension))
{ {
const proxiedPath = parts.join("/"); console.log("REGULAR", parts);
const transpiled = await BakeCheck(RootRunning, proxiedPath, proxiedPath+url.search); const transpiled = await BakeCheck({path:"."+url.pathname, bundle:false});
//return JSResponse(transpiled[0]); //return JSResponse(transpiled[0]);
return JSResponse(transpiled[url.searchParams.has(keyReload) ? 0 : 1]); return JSResponse(transpiled[url.searchParams.has(keyReload) ? 0 : 1]);
} }
@ -229,15 +241,17 @@ const Watcher =async()=>
{ {
const key = path.substring(cutOff).replaceAll("\\", "/"); const key = path.substring(cutOff).replaceAll("\\", "/");
const keyAbs = "/"+key;
const keyRel = "./"+key;
console.log("File change", path, key); console.log("File change", path, key);
if(action != "remove") if(action != "remove")
{ {
await BakeForce(RootRunning, key, key); await BakeForce({path:keyRel, bundle:false});
SocketsSend(key); SocketsSend(keyRel);
} }
else else
{ {
delete BakeCache[key]; delete BakeCache[keyRel];
} }
} }
} }