pass query to user files

This commit is contained in:
Seth Trowbridge 2025-10-18 10:24:47 -04:00
parent d925b8d75e
commit 22aaa7cfd6
2 changed files with 38 additions and 41 deletions

View File

@ -100,7 +100,9 @@ const findNextExport =(inFile:string, inIndex=0, inLocal:Array<string>, inForeig
} }
}; };
export const ModuleShape =(inText:string)=> export type Shape = [local:string[], foreign:string[]];
export const ModuleShape =(inText:string):Shape=>
{ {
let match = 0 as number|false; let match = 0 as number|false;
let count = 0; let count = 0;
@ -111,21 +113,7 @@ export const ModuleShape =(inText:string)=>
count++; count++;
match = findNextExport(inText, match, local, foreign); match = findNextExport(inText, match, local, foreign);
} }
return[local, foreign] as [local:string[], foreign:string[]]; return[local, foreign];
};
export const ModuleProxy =(inText:string, inPath:string, reactProxy:string, reloadKey:string)=>
{
const [local, foreign] = ModuleShape(inText);
return `
import {FileListen} from "${reactProxy}";
import * as Import from "${inPath}?${reloadKey}=${new Date().getTime()}";
${ local.map(m=>`let proxy_${m} = Import.${m}; export { proxy_${m} as ${m} };`).join("\n") }
FileListen("${inPath}", (updatedModule)=>
{
${ local.map(m=>`proxy_${m} = updatedModule.${m};`).join("\n\t") }
});
${ foreign.join(";\n") }`;
}; };

View File

@ -1,10 +1,11 @@
import { contentType } from "jsr:@std/media-types"; import { contentType } from "jsr:@std/media-types";
import { ModuleProxy } from "./hmr/hmr-static.tsx"; import { ModuleShape, type Shape } from "./hmr/hmr-static.tsx";
const keyBundle = "=>"; const keyBundle = "=>";
const keyTranspile = "->"; const keyTranspile = "->";
const keyReload = "hmr:id"; const keyReload = "hmr:id";
const keysExtension = ["ts", "tsx"]; const keysExtension = ["ts", "tsx"];
const keyReactProxy = `/${keyTranspile}/${import.meta.resolve("./hmr/hmr-listen.tsx")}`
const extractExtension =(path:string)=> const extractExtension =(path:string)=>
{ {
const ind = path.lastIndexOf("."); const ind = path.lastIndexOf(".");
@ -28,15 +29,13 @@ 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 = {
path:string, key:string,
key?:string,
bundle?:boolean bundle?:boolean
}; };
async function BakeForce(options:FullBakeConfig) async function BakeForce(options:FullBakeConfig)
{ {
const path = options.path; const key = options.key;
const key = options.key || path;
const type = options.bundle; 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.)
@ -46,7 +45,7 @@ async function BakeForce(options:FullBakeConfig)
} }
// 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:[path]}; const config = {...(type ? bakeConfigPackage : bakeConfigLocal), entrypoints:[key]};
console.log("baking", config.entrypoints, "as", key); 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
@ -58,15 +57,14 @@ async function BakeForce(options:FullBakeConfig)
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 listenerImport = `/${keyTranspile}/${import.meta.resolve("./hmr/hmr-listen.tsx")}`; const save:CachedTranspile = [body, type ? undefined : ModuleShape(body)];
const save:CachedTranspile = [body, type ? "" : ModuleProxy(body, path, listenerImport, keyReload)];
BakeCache[key] = save; // replace promise with resolved value BakeCache[key] = save; // replace promise with resolved value
return save; return save;
} }
} }
catch (e) catch (e)
{ {
console.log("Bake error", path, e); console.log("Bake error", key, e);
} }
// failed - remove cache entry so next attempt can retry // failed - remove cache entry so next attempt can retry
delete BakeCache[key]; delete BakeCache[key];
@ -92,7 +90,7 @@ async function BakeCheck(options:FullBakeConfig)
} }
return lookup as CachedTranspile; return lookup as CachedTranspile;
} }
type CachedTranspile = [file:string, profile:string] type CachedTranspile = [file:string, shape?:Shape]
// BakeCache may hold a resolved cached tuple or an in-flight Promise that resolves to one. // BakeCache may hold a resolved cached tuple or an in-flight Promise that resolves to one.
const BakeCache:Record<string, CachedTranspile | Promise<CachedTranspile|undefined> | undefined> = {} const BakeCache:Record<string, CachedTranspile | Promise<CachedTranspile|undefined> | undefined> = {}
@ -111,15 +109,6 @@ const reactValueModified = `/${keyTranspile}/${import.meta.resolve("./hmr/hmr-re
denoBody.imports[reactKey] = reactValueModified denoBody.imports[reactKey] = reactValueModified
denoBody.imports[reactKey+"/jsx-runtime"] = reactValueModified; denoBody.imports[reactKey+"/jsx-runtime"] = reactValueModified;
/*
npm:react | bundle | no-prefix
hrm/hmr-listen.tsx | transpile | server-prefix
local/file/component.tsx | transpile | local-prefix
*/
console.log(denoBody.imports); console.log(denoBody.imports);
const importMap = `<script type="importmap">{"imports":${JSON.stringify(denoBody.imports, null, 2)}}</script>`; const importMap = `<script type="importmap">{"imports":${JSON.stringify(denoBody.imports, null, 2)}}</script>`;
@ -175,7 +164,7 @@ Deno.serve(async(req:Request)=>
{ {
console.log("BUNDLE", parts); console.log("BUNDLE", parts);
const transpiled = await BakeCheck({ const transpiled = await BakeCheck({
path: parts.slice(1).join("/"), key: parts.slice(1).join("/"),
bundle:true bundle:true
}); });
return JSResponse(transpiled[0]); return JSResponse(transpiled[0]);
@ -184,7 +173,7 @@ Deno.serve(async(req:Request)=>
{ {
console.log("TRANSPILE", parts); console.log("TRANSPILE", parts);
const transpiled = await BakeCheck({ const transpiled = await BakeCheck({
path:parts.slice(1).join("/"), key:parts.slice(1).join("/"),
bundle:false bundle:false
}); });
return JSResponse(transpiled[0]); return JSResponse(transpiled[0]);
@ -192,9 +181,29 @@ Deno.serve(async(req:Request)=>
if(keysExtension.includes(extension)) if(keysExtension.includes(extension))
{ {
console.log("REGULAR", parts); console.log("REGULAR", parts);
const transpiled = await BakeCheck({path:"./"+parts.join("/"), bundle:false}); const key = "./"+parts.join("/");
//return JSResponse(transpiled[0]);
return JSResponse(transpiled[url.searchParams.has(keyReload) ? 0 : 1]); const [body, [local, foreign]] = await BakeCheck({key, bundle:false});
if(url.searchParams.has(keyReload))
{
return JSResponse(body);
}
else
{
const params = new URLSearchParams(url.searchParams);
params.set(keyReload, new Date().getTime().toString());
return JSResponse(`
import {FileListen} from "${keyReactProxy}";
import * as Import from "${key + "?" + params.toString()}";
${ local.map(m=>`let proxy_${m} = Import.${m}; export { proxy_${m} as ${m} };`).join("\n") }
FileListen("${key}", (updatedModule)=>
{
${ local.map(m=>`proxy_${m} = updatedModule.${m};`).join("\n\t") }
});
${ foreign.join(";\n") }`
);
}
} }
if(!extension) if(!extension)
@ -249,7 +258,7 @@ const Watcher =async()=>
console.log("File change", path, key); console.log("File change", path, key);
if(action != "remove") if(action != "remove")
{ {
await BakeForce({path:keyRel, bundle:false}); await BakeForce({key:keyRel, bundle:false});
SocketsSend(keyRel); SocketsSend(keyRel);
} }
else else