boot-function #1

Merged
SethTrowbridge merged 25 commits from boot-function into master 2023-06-21 07:53:14 -04:00
7 changed files with 117 additions and 71 deletions
Showing only changes of commit 47ef75c55b - Show all commits

15
_lib_/boot.tsx Normal file
View File

@ -0,0 +1,15 @@
import "../serve.tsx";
Deno.args.forEach(arg=>
{
if(arg.startsWith("--"))
{
const kvp = arg.substring(2).split("=");
Deno.env.set(kvp[0], kvp[1] || "true");
}
});
if(Deno.env.get("dev"))
{
await import("../local.tsx");
}

View File

@ -24,6 +24,42 @@ export const Shadow =(inElement:HTMLElement, inConfig?:TW.TwindUserConfig)=>
return ShadowDiv; return ShadowDiv;
}; };
let booted = false;
export const Boot =async(inSettings:{App:()=>React.JSX.Element, CSS?:TW.TwindUserConfig, DOM?:string})=>
{
if(booted){return;}
booted = true;
const settings = {CSS:{...Configure, ...inSettings.CSS||{} }, DOM:inSettings.DOM||"#app", App:inSettings.App};
console.log("Clinet boot called")
let dom = document.querySelector(settings.DOM);
if(!dom)
{
console.log(`element "${settings.DOM}" not found.`);
return false;
}
dom = Shadow(dom as HTMLElement, settings.CSS)
const app = React.createElement(()=> React.createElement(settings.App, null), null);
if(React.render)
{
React.render(app, dom);
return ()=>dom && React.unmountComponentAtNode(dom);
}
else
{
const reactDom = await import(`https://esm.sh/react-dom@${React.version}/client`);
const root = reactDom.createRoot(dom);
root.render(app);
return root.unmount;
}
};
export default async(inSelector:string, inModulePath:string, inMemberApp="default", inMemberCSS="CSS"):Promise<(()=>void)|false>=> export default async(inSelector:string, inModulePath:string, inMemberApp="default", inMemberCSS="CSS"):Promise<(()=>void)|false>=>
{ {
let dom = document.querySelector(inSelector); let dom = document.querySelector(inSelector);

View File

@ -1,23 +0,0 @@
{
"version": "2",
"remote": {
"https://esm.sh/preact@10.15.1": "2b79349676a4942fbcf835c4efa909791c2f0aeca195225bf22bac9866e94b4e",
"https://esm.sh/preact@10.15.1/compat": "07273e22b1c335b8acc9f33c5e78165319c59bd8e2d0f3e5a2b4e028329424d9",
"https://esm.sh/react@18.2.0": "742d8246041966ba1137ec8c60888c35882a9d2478bce63583875f86c1e3687c",
"https://esm.sh/stable/preact@10.15.1/denonext/compat.js": "bad6b5b4d4fdfa5975b7a8d30410bd6877247f058e4952799fab39f66a94b8cf",
"https://esm.sh/stable/preact@10.15.1/denonext/hooks.js": "5c989ad368cf4f2cb3a5d7d1801843d9348c599fe3e7731d04728f7b845d724e",
"https://esm.sh/stable/preact@10.15.1/denonext/preact.mjs": "30710ac1d5ff3711ae0c04eddbeb706f34f82d97489f61aaf09897bc75d2a628",
"https://esm.sh/stable/react@18.2.0/deno/react.mjs": "a5a73ee24acca4744ee22c51d9357f31968d1f684ce253bde222b4e26d09f49f",
"https://esm.sh/v118/@types/prop-types@15.7.5/index.d.ts": "6a386ff939f180ae8ef064699d8b7b6e62bc2731a62d7fbf5e02589383838dea",
"https://esm.sh/v118/@types/react@18.2.0/global.d.ts": "549df62b64a71004aee17685b445a8289013daf96246ce4d9b087d13d7a27a61",
"https://esm.sh/v118/@types/react@18.2.0/index.d.ts": "b091747b1f503f434d3cac4217a13858baba87b421a7054ffdfd797da7737678",
"https://esm.sh/v118/@types/scheduler@0.16.3/tracing.d.ts": "f5a8b384f182b3851cec3596ccc96cb7464f8d3469f48c74bf2befb782a19de5",
"https://esm.sh/v118/csstype@3.1.2/index.d.ts": "4c68749a564a6facdf675416d75789ee5a557afda8960e0803cf6711fa569288",
"https://esm.sh/v118/preact@10.15.1/compat/src/index.d.ts": "9ec63be9612a10ff72fd4183179cde7d551ce43b3a0c2f549d8f788910d8263d",
"https://esm.sh/v118/preact@10.15.1/compat/src/suspense-list.d.ts": "b8e274324392157ce476ef3a48ae215c9f7003b08525d140645f19eab20d1948",
"https://esm.sh/v118/preact@10.15.1/compat/src/suspense.d.ts": "81f5266e0977a94347505d11b8103024211f2b4f3b2eb2aa276a10d8fd169e65",
"https://esm.sh/v118/preact@10.15.1/hooks/src/index.d.ts": "933eab6436614f8cd8e9b7c9b8bd54c6f3f14c3f065ba42c8c87a42d93de6595",
"https://esm.sh/v118/preact@10.15.1/src/index.d.ts": "fa83186a4b6caca36d52ca2d49b481c3ca5460988d4a8388d44dadc28987fb27",
"https://esm.sh/v118/preact@10.15.1/src/jsx.d.ts": "a6e4b7e4af3b959f8cfd41a0f475c547807ebcec8524d9605ab5c6de79f302fa"
}
}

View File

@ -1,3 +1,4 @@
import "../_lib_/boot.tsx";
import React from "react"; import React from "react";
const CTXString = React.createContext("lol"); const CTXString = React.createContext("lol");
@ -30,13 +31,13 @@ const builder =(inState:Store):Store=>
return inState; return inState;
} }
export default ()=> export default ()=>
{ {
const [Store, Dispatch] = React.useReducer(reducer, {name:"seth", age:24} as Store, builder) const [Store, Dispatch] = React.useReducer(reducer, {name:"seth", age:24} as Store, builder)
return <CTXString.Provider value="intradestink"> return <CTXString.Provider value="intradestink">
<div class="my-4 font-sans"> <div class="my-4 font-sans">
<h1 class="font-black text-xl">Title!!!!</h1> <h1 class="font-black text-xl text-red-500">Title?</h1>
<h2>subtitle!</h2> <h2>subtitle!</h2>
<p> <p>
<button onClick={e=>Dispatch(1)}>{Store.name}|{Store.age}?</button> <button onClick={e=>Dispatch(1)}>{Store.name}|{Store.age}?</button>
@ -48,5 +49,5 @@ export default ()=>
<Outer> <Outer>
<Inner/> <Inner/>
</Outer> </Outer>
</CTXString.Provider> </CTXString.Provider>;
} }

View File

@ -2,12 +2,11 @@
"compilerOptions": { "lib": ["deno.window", "dom"] }, "compilerOptions": { "lib": ["deno.window", "dom"] },
"imports": "imports":
{ {
"react":"https://esm.sh/preact@10.15.1/compat", "react":"https://esm.sh/preact@10.15.1/compat"
"@app": "./app.tsx"
}, },
"tasks": "tasks":
{ {
"local": "deno run -A --no-lock ../local.tsx", "local": "deno run -A --no-lock app.tsx --dev",
"serve": "deno run -A --no-lock ../serve.tsx" "serve": "deno run -A --no-lock app.tsx"
} }
} }

View File

@ -2,10 +2,8 @@ import {Configure, Transpile, Extension} from "./serve.tsx";
const SocketsLive:Set<WebSocket> = new Set(); const SocketsLive:Set<WebSocket> = new Set();
const SocketsSend =(inData:string)=>{ console.log(inData); for (const socket of SocketsLive){ socket.send(inData); } } const SocketsSend =(inData:string)=>{ console.log(inData); for (const socket of SocketsLive){ socket.send(inData); } }
const Directory = `file://${Deno.cwd().replaceAll("\\", "/")}`;
Configure({ Configure({
Proxy:Directory,
SWCOp: SWCOp:
{ {
sourceMaps: "inline", sourceMaps: "inline",
@ -36,11 +34,11 @@ Configure({
console.log(inImports); console.log(inImports);
return inImports; return inImports;
}, },
async Serve(inReq, inURL, inExt, inMap) async Serve(inReq, inURL, inExt, inMap, inProxy)
{ {
if(Transpile.Check(inExt) && !inURL.searchParams.get("reload") && !inURL.pathname.startsWith("/_lib_/")) if(Transpile.Check(inExt) && !inURL.searchParams.get("reload") && !inURL.pathname.startsWith("/_lib_/"))
{ {
const imp = await import(Directory+inURL.pathname); const imp = await import(inProxy+inURL.pathname);
const members = []; const members = [];
for( const key in imp ) { members.push(key); } for( const key in imp ) { members.push(key); }
@ -73,35 +71,40 @@ FileListen("${inURL.pathname}", (updatedModule)=>
} }
}); });
let blocking = false; const Watcher =async()=>
const filesChanged:Map<string, string> = new Map();
for await (const event of Deno.watchFs(Deno.cwd()))
{ {
event.paths.forEach( path => filesChanged.set(path, event.kind) ); let blocking = false;
if(!blocking) const filesChanged:Map<string, string> = new Map();
for await (const event of Deno.watchFs(Deno.cwd()))
{ {
blocking = true; event.paths.forEach( path => filesChanged.set(path, event.kind) );
setTimeout(async()=> if(!blocking)
{ {
for await (const [path, action] of filesChanged) blocking = true;
setTimeout(async()=>
{ {
if(Transpile.Check(Extension(path))) for await (const [path, action] of filesChanged)
{ {
const key = path.substring(Deno.cwd().length).replaceAll("\\", "/"); if(Transpile.Check(Extension(path)))
if(action != "remove")
{ {
const tsx = await Transpile.Fetch(Directory+key, key, true); const key = path.substring(Deno.cwd().length).replaceAll("\\", "/");
tsx && SocketsSend(key); if(action != "remove")
} {
else const tsx = await Transpile.Fetch(`file://${Deno.cwd().replaceAll("\\", "/")}`+key, key, true);
{ tsx && SocketsSend(key);
Transpile.Cache.delete(key); }
else
{
Transpile.Cache.delete(key);
}
} }
} }
filesChanged.clear();
blocking = false;
} }
filesChanged.clear(); , 1000);
blocking = false;
} }
, 1000);
} }
} }
Watcher().then(()=>console.log("done watching"));

View File

@ -23,16 +23,16 @@ const ImportMapReload =async()=>
ImportMap.imports = Configuration.Remap(json.imports); ImportMap.imports = Configuration.Remap(json.imports);
}; };
type CustomHTTPHandler = (inReq:Request, inURL:URL, inExt:string|false, inMap:{imports:Record<string, string>})=>void|false|Response|Promise<Response|void|false>; type CustomHTTPHandler = (inReq:Request, inURL:URL, inExt:string|false, inMap:{imports:Record<string, string>}, inProxy:string)=>void|false|Response|Promise<Response|void|false>;
type CustomRemapper = (inImports:Record<string, string>)=>Record<string, string>; type CustomRemapper = (inImports:Record<string, string>)=>Record<string, string>;
type Configuration = {Proxy:string, Allow:string, Reset:string, SWCOp:SWCW.Options, Serve:CustomHTTPHandler, Shell:CustomHTTPHandler, Remap:CustomRemapper}; type Configuration = {Proxy:string, Allow:string, Reset:string, SWCOp:SWCW.Options, Serve:CustomHTTPHandler, Shell:CustomHTTPHandler, Remap:CustomRemapper};
type ConfigurationArgs = {Proxy?:string, Allow?:string, Reset?:string, SWCOp?:SWCW.Options, Serve?:CustomHTTPHandler, Shell?:CustomHTTPHandler, Remap?:CustomRemapper}; type ConfigurationArgs = {Proxy?:string, Allow?:string, Reset?:string, SWCOp?:SWCW.Options, Serve?:CustomHTTPHandler, Shell?:CustomHTTPHandler, Remap?:CustomRemapper};
let Configuration:Configuration = let Configuration:Configuration =
{ {
Proxy: `file://${Deno.cwd().replaceAll("\\", "/")}`, Proxy: new URL(`file://${Deno.cwd().replaceAll("\\", "/")}`).toString(),
Allow: "*", Allow: "*",
Reset: "/clear-cache", Reset: "/clear-cache",
Serve(inReq, inURL, inExt, inMap){}, Serve(inReq, inURL, inExt, inMap, inProxy){},
Remap: (inImports)=> Remap: (inImports)=>
{ {
Object.entries(inImports).forEach(([key, value])=> Object.entries(inImports).forEach(([key, value])=>
@ -51,8 +51,13 @@ let Configuration:Configuration =
console.log(inImports); console.log(inImports);
return inImports; return inImports;
}, },
Shell(inReq, inURL, inExt, inMap) Shell(inReq, inURL, inExt, inMap, inProxy)
{ {
console.log("Start app:", Deno.mainModule, "start dir", inProxy);
console.log("Split:", Deno.mainModule.split(inProxy) );
const parts = Deno.mainModule.split(inProxy);
return new Response( return new Response(
`<!doctype html> `<!doctype html>
<html> <html>
@ -62,8 +67,8 @@ let Configuration:Configuration =
<div id="app"></div> <div id="app"></div>
<script type="importmap">${JSON.stringify(inMap)}</script> <script type="importmap">${JSON.stringify(inMap)}</script>
<script type="module"> <script type="module">
import Mount from "/_lib_/mount.tsx"; import Mount from "/_lib_/mount.tsx";
Mount("#app", "@app"); Mount("#app", "${parts[1]??"/app.tsx"}");
</script> </script>
</body> </body>
</html>`, {status:200, headers:{"content-type":"text/html"}}); </html>`, {status:200, headers:{"content-type":"text/html"}});
@ -162,7 +167,7 @@ HTTP.serve(async(req: Request)=>
} }
// allow for custom handler // allow for custom handler
const custom = await Configuration.Serve(req, url, ext, ImportMap); const custom = await Configuration.Serve(req, url, ext, ImportMap, Configuration.Proxy);
if(custom) if(custom)
{ {
return custom; return custom;
@ -171,26 +176,36 @@ HTTP.serve(async(req: Request)=>
// transpileable files // transpileable files
if(Transpile.Check(ext)) if(Transpile.Check(ext))
{ {
let code;
let path;
if(url.pathname.startsWith("/_lib_/")) if(url.pathname.startsWith("/_lib_/"))
{ {
const path = import.meta.url+"/.."+url.pathname; if(url.pathname.endsWith("boot.tsx"))
const code = await Transpile.Fetch(path, url.pathname, true);
if(code)
{ {
return new Response(code, {headers:{"content-type":"application/javascript"}}); path = import.meta.url+"/../_lib_/mount.tsx";
} }
else
{
path = import.meta.url+"/.."+url.pathname;
}
code = await Transpile.Fetch(path, url.pathname, true);
} }
else else
{ {
const lookup = await Transpile.Fetch(Configuration.Proxy + url.pathname, url.pathname); path = Configuration.Proxy + url.pathname;
return new Response(lookup, {status:lookup?200:404, headers:{...headers, "content-type":"application/javascript"}} ); code = await Transpile.Fetch(path, url.pathname);
}
if(code)
{
return new Response(code, {headers:{...headers, "content-type":"application/javascript"}} );
} }
} }
// custom page html // custom page html
if(!ext) if(!ext)
{ {
const shell = await Configuration.Shell(req, url, ext, ImportMap); const shell = await Configuration.Shell(req, url, ext, ImportMap, Configuration.Proxy);
if(shell) if(shell)
{ {
return shell; return shell;