remove preact, GPT fixes
This commit is contained in:
parent
5bb8ba29a4
commit
102f8a74d2
5
app.tsx
5
app.tsx
@ -4,12 +4,11 @@ import React from "react";
|
||||
|
||||
|
||||
export function App(){
|
||||
console.log(React.useState(1))
|
||||
|
||||
const [countGet, countSet] = React.useState(0);
|
||||
const [countGet, countSet] = React.useState(2);
|
||||
|
||||
return <>
|
||||
<p style={{padding:"2rem", background:"blue", color:"white"}}>test paragraph</p>
|
||||
<p style={{padding:"2rem", background:"red", color:"white"}}>test paragraph</p>
|
||||
<button onClick={()=>{countSet(countGet+1)}}>{countGet}</button>
|
||||
</>
|
||||
}
|
||||
|
@ -13,9 +13,6 @@
|
||||
"react":"https://esm.sh/react@19.2.0",
|
||||
"react/":"https://esm.sh/react@19.2.0/",
|
||||
"react-dom/":"https://esm.sh/react-dom@19.2.0/",
|
||||
|
||||
"@preact/":"npm:@preact/",
|
||||
"signals-original": "npm:@preact/signals",
|
||||
"react-original": "https://esm.sh/react@19.2.0"
|
||||
}
|
||||
}
|
||||
|
35
deno.lock
35
deno.lock
@ -1,26 +1,25 @@
|
||||
{
|
||||
"version": "5",
|
||||
"specifiers": {
|
||||
"npm:@preact/signals@*": "2.3.2_preact@10.27.2"
|
||||
"jsr:@std/media-types@*": "1.1.0"
|
||||
},
|
||||
"npm": {
|
||||
"@preact/signals-core@1.12.1": {
|
||||
"integrity": "sha512-BwbTXpj+9QutoZLQvbttRg5x3l5468qaV2kufh+51yha1c53ep5dY4kTuZR35+3pAZxpfQerGJiQqg34ZNZ6uA=="
|
||||
},
|
||||
"@preact/signals@2.3.2_preact@10.27.2": {
|
||||
"integrity": "sha512-Q22avIn4z0BQnmFeo6Y5HCnJTo8VufN84zN51OtqeNgZOVCYgdwEOcJKVX1x/IrjRVxUnOy6Ubn7H5aVFujXaQ==",
|
||||
"dependencies": [
|
||||
"@preact/signals-core",
|
||||
"preact"
|
||||
]
|
||||
},
|
||||
"preact@10.27.2": {
|
||||
"integrity": "sha512-5SYSgFKSyhCbk6SrXyMpqjb5+MQBgfvEKE/OC+PujcY34sOpqtr+0AZQtPYx5IA6VxynQ7rUPCtKzyovpj9Bpg=="
|
||||
"jsr": {
|
||||
"@std/media-types@1.1.0": {
|
||||
"integrity": "c9d093f0c05c3512932b330e3cc1fe1d627b301db33a4c2c2185c02471d6eaa4"
|
||||
}
|
||||
},
|
||||
"workspace": {
|
||||
"dependencies": [
|
||||
"npm:@preact/signals@*"
|
||||
]
|
||||
"redirects": {
|
||||
"https://esm.sh/scheduler@^0.27.0?target=denonext": "https://esm.sh/scheduler@0.27.0?target=denonext"
|
||||
},
|
||||
"remote": {
|
||||
"https://esm.sh/react-dom@19.2.0/client": "489f23e19c36110c4d6361c09cd969e304665799d974ddc90ea36d9a7346e249",
|
||||
"https://esm.sh/react-dom@19.2.0/denonext/client.mjs": "d12f88ffe04230ba1e9722c97157d86b8e287e71911de06488187abbdd216726",
|
||||
"https://esm.sh/react-dom@19.2.0/denonext/react-dom.mjs": "d7bae53f5407fc0f29e04fd1866deb4a17e4e0df5ede1cdc50c791fbe21c3b06",
|
||||
"https://esm.sh/react@19.2.0": "c69a4f37d5f2e04b5088c1fe6bc24f6bf0728704544f902b553d0852a7fdfae0",
|
||||
"https://esm.sh/react@19.2.0/denonext/jsx-runtime.mjs": "75cd15682ab2ac7ffa971367359974541bbcfc8af7217c3fdb62f2a21b9be765",
|
||||
"https://esm.sh/react@19.2.0/denonext/react.mjs": "6ffb8433fe90ae15017245e8b461f5824590e5dee07568afe31bab19e10b03ae",
|
||||
"https://esm.sh/react@19.2.0/jsx-runtime": "11cfe447d2aed82e4469f0b856899b053d5fe1daf059d53cbfa75c5e5b706acc",
|
||||
"https://esm.sh/scheduler@0.27.0/denonext/scheduler.mjs": "b1ba1723bf253d62b0aa1c3c0603d0d025010642158b0e746ae1fd6f7500deeb",
|
||||
"https://esm.sh/scheduler@0.27.0?target=denonext": "d5e7a8b3ddf1c77fdd56083413b9ed29648e017b1000b85b3d3059a226627742"
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { HMR } from "./hmr-react.tsx";
|
||||
import { GroupSignal, GroupSignalHook } from "./hmr-signal.tsx";
|
||||
|
||||
const FileListeners = new Map() as Map<string, Array<(module:unknown)=>void>>;
|
||||
export const FileListen =(inPath:string, inHandler:()=>void)=>
|
||||
@ -14,18 +13,9 @@ Socket.addEventListener('message', async(event:{data:string})=>
|
||||
{
|
||||
// When a file changes, dynamically re-import it to get the updated members
|
||||
// send the updated members to any listeners for that file
|
||||
|
||||
GroupSignal.reset();
|
||||
|
||||
const reImport = await import(document.location.origin+event.data+"?reload="+Math.random());
|
||||
FileListeners.get(event.data)?.forEach(reExport=>reExport(reImport));
|
||||
|
||||
GroupSignal.swap();
|
||||
|
||||
GroupSignalHook.reset();
|
||||
HMR.update();
|
||||
GroupSignalHook.reset();
|
||||
|
||||
});
|
||||
Socket.addEventListener("error", ()=>{clearInterval(SocketTimer); console.log("HMR socket lost")})
|
||||
const SocketTimer = setInterval(()=>{Socket.send("ping")}, 5000);
|
@ -1,47 +0,0 @@
|
||||
import * as SignalsParts from "signals-original";
|
||||
import DeepEqual from "https://esm.sh/deep-eql@4.1.3";
|
||||
|
||||
type Entry<T> = [signal:SignalsParts.Signal<T>, initArg:T];
|
||||
|
||||
function ProxyGroup<T>(inFunc:(initArg:T)=>SignalsParts.Signal<T>)
|
||||
{
|
||||
let recordEntry:Entry<T>[] = [];
|
||||
let recordEntryNew:Entry<T>[] = [];
|
||||
let recordIndex = 0;
|
||||
const reset =()=> recordIndex = 0;
|
||||
const swap =()=>
|
||||
{
|
||||
recordEntry = recordEntryNew;
|
||||
recordEntryNew = [] as Entry<T>[];
|
||||
};
|
||||
const proxy =(arg:T)=>
|
||||
{
|
||||
const lookupOld = recordEntry[recordIndex];
|
||||
if(lookupOld && DeepEqual(lookupOld[1], arg))
|
||||
{
|
||||
recordEntryNew[recordIndex] = lookupOld;
|
||||
recordIndex++;
|
||||
return lookupOld[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
const sig = inFunc(arg);
|
||||
recordEntryNew[recordIndex] = [sig, arg];
|
||||
recordEntry[recordIndex] = [sig, arg];
|
||||
recordIndex++;
|
||||
return sig;
|
||||
}
|
||||
};
|
||||
return {reset, swap, proxy};
|
||||
}
|
||||
|
||||
export const GroupSignal = ProxyGroup(SignalsParts.signal);
|
||||
export const GroupSignalHook = ProxyGroup(SignalsParts.useSignal);
|
||||
|
||||
|
||||
const proxySignal = GroupSignal.proxy;
|
||||
const proxySignalHook = GroupSignalHook.proxy;
|
||||
|
||||
export * from "signals-original";
|
||||
export { proxySignal as signal, proxySignalHook as useSignal };
|
||||
export default {...SignalsParts, signal:proxySignal, useSignal:proxySignalHook};
|
12
index.html
12
index.html
@ -1,12 +1,10 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Document</title>
|
||||
</head>
|
||||
<body>
|
||||
<script type="module">
|
||||
import "/app.tsx";
|
||||
</script>
|
||||
</body>
|
||||
</head>
|
||||
<body>
|
||||
<script type="module" src="/app.tsx"></script>
|
||||
</body>
|
||||
</html>
|
96
server.ts
96
server.ts
@ -28,31 +28,61 @@ const bakeConfigPackage:Deno.bundle.Options =
|
||||
const bakeConfigLocal:Deno.bundle.Options = {...bakeConfigPackage, minify:false, sourcemap:"inline", inlineImports:false };
|
||||
async function BakeForce(path:string, type?:"package")
|
||||
{
|
||||
const config = type ? bakeConfigPackage : bakeConfigLocal;
|
||||
// If already baking, return the in-flight promise. (Caller may also call BakeCheck which handles this.)
|
||||
if (BakeCache[path] && typeof (BakeCache[path] as any)?.then === "function")
|
||||
{
|
||||
return await BakeCache[path] as CachedTranspile | undefined;
|
||||
}
|
||||
|
||||
// Create a fresh config per bake to avoid shared mutation.
|
||||
const config = {...(type ? bakeConfigPackage : bakeConfigLocal)};
|
||||
config.entrypoints = [...config.entrypoints];
|
||||
config.entrypoints[0] = type ? path : RootRunning+path;
|
||||
console.log("baking", config.entrypoints);
|
||||
const result = await Deno.bundle(config);
|
||||
|
||||
if(result.outputFiles)
|
||||
// store the in-flight promise immediately so concurrent callers reuse it
|
||||
const inflight = (async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
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;
|
||||
BakeCache[path] = save; // replace promise with resolved value
|
||||
return save;
|
||||
}
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
console.log("BakeForce error for", path, e);
|
||||
}
|
||||
// failed - remove cache entry so next attempt can retry
|
||||
delete BakeCache[path];
|
||||
return undefined;
|
||||
})();
|
||||
|
||||
BakeCache[path] = inflight;
|
||||
return await inflight;
|
||||
};
|
||||
async function BakeCheck(path:string, type?:"package")
|
||||
{
|
||||
const lookup:CachedTranspile = await BakeCache[path];
|
||||
const lookup = BakeCache[path];
|
||||
if(!lookup)
|
||||
{
|
||||
return await BakeForce(path, type);
|
||||
}
|
||||
return lookup;
|
||||
// if an in-flight promise is stored, await it
|
||||
if (typeof (lookup as any)?.then === "function")
|
||||
{
|
||||
return await lookup as CachedTranspile | undefined;
|
||||
}
|
||||
return lookup as CachedTranspile;
|
||||
}
|
||||
type CachedTranspile = [file:string, profile:string]
|
||||
const BakeCache:Record<string, CachedTranspile> = {}
|
||||
// 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 denoBody = await fetch(RootRunning+"/deno.json").then(resp=>resp.json());
|
||||
|
||||
@ -78,7 +108,18 @@ 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);
|
||||
const JSResponse =(body:string)=>
|
||||
{
|
||||
const encoder = new TextEncoder();
|
||||
const stream = new ReadableStream({
|
||||
start(controller)
|
||||
{
|
||||
controller.enqueue(encoder.encode(body));
|
||||
controller.close();
|
||||
}
|
||||
});
|
||||
return new Response(stream, JSHead);
|
||||
}
|
||||
|
||||
Deno.serve(async(req:Request)=>
|
||||
{
|
||||
@ -90,7 +131,7 @@ Deno.serve(async(req:Request)=>
|
||||
socket.onopen = () => SocketsLive.add(socket);
|
||||
socket.onclose = () => SocketsLive.delete(socket);
|
||||
socket.onmessage = () => {};
|
||||
socket.onerror = (e) => console.log("Socket errored:", e);
|
||||
socket.onerror = (e) => {console.log("Socket errored:", e);SocketsLive.delete(socket);}
|
||||
return response;
|
||||
}
|
||||
catch(e){ console.log("Socket errored:", e); }
|
||||
@ -99,7 +140,12 @@ Deno.serve(async(req:Request)=>
|
||||
const url = new URL(req.url);
|
||||
const parts = url.pathname.split("/").filter(part=>part);
|
||||
|
||||
const lastPart = parts.at(-1);
|
||||
// if there are no path segments, serve index immediately (avoid calling extractExtension on undefined)
|
||||
if(parts.length === 0)
|
||||
{
|
||||
return IndexResponse();
|
||||
}
|
||||
const lastPart = parts.at(-1) ?? "";
|
||||
const extension = extractExtension(lastPart);
|
||||
|
||||
if(parts[0] == keyBundle)
|
||||
@ -128,8 +174,33 @@ Deno.serve(async(req:Request)=>
|
||||
return new Response();
|
||||
});
|
||||
|
||||
|
||||
|
||||
const SocketsLive:Set<WebSocket> = new Set();
|
||||
const SocketsSend =(inData:string)=>{ for (const socket of SocketsLive){ socket.send(inData); } }
|
||||
const SocketsSend =(inData:string)=>
|
||||
{
|
||||
// iterate over a snapshot so we can remove while iterating
|
||||
for (const socket of Array.from(SocketsLive))
|
||||
{
|
||||
try
|
||||
{
|
||||
if (socket.readyState === WebSocket.OPEN)
|
||||
{
|
||||
socket.send(inData);
|
||||
}
|
||||
else
|
||||
{
|
||||
// not open any more — remove it
|
||||
SocketsLive.delete(socket);
|
||||
}
|
||||
}
|
||||
catch (err)
|
||||
{
|
||||
console.log("Failed to send to socket:", err);
|
||||
SocketsLive.delete(socket);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const Watcher =async()=>
|
||||
{
|
||||
@ -143,7 +214,8 @@ const Watcher =async()=>
|
||||
blocking = true;
|
||||
setTimeout(async()=>
|
||||
{
|
||||
for await (const [path, action] of filesChanged)
|
||||
// filesChanged is a Map, iterate normally
|
||||
for (const [path, action] of filesChanged)
|
||||
{
|
||||
const extension = extractExtension(path);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user