diff --git a/_lib_/boot.tsx b/_lib_/boot.tsx new file mode 100644 index 0000000..c510a00 --- /dev/null +++ b/_lib_/boot.tsx @@ -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"); +} \ No newline at end of file diff --git a/_lib_/mount.tsx b/_lib_/mount.tsx index 0314212..ab5e1ca 100644 --- a/_lib_/mount.tsx +++ b/_lib_/mount.tsx @@ -24,6 +24,42 @@ export const Shadow =(inElement:HTMLElement, inConfig?:TW.TwindUserConfig)=> 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>=> { let dom = document.querySelector(inSelector); @@ -50,4 +86,4 @@ export default async(inSelector:string, inModulePath:string, inMemberApp="defaul return root.unmount; } -}; \ No newline at end of file +}; diff --git a/deno.lock b/deno.lock deleted file mode 100644 index 6c863c0..0000000 --- a/deno.lock +++ /dev/null @@ -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" - } -} diff --git a/example/app.tsx b/example/app.tsx index 716c553..eeeac98 100644 --- a/example/app.tsx +++ b/example/app.tsx @@ -1,3 +1,4 @@ +import "../_lib_/boot.tsx"; import React from "react"; const CTXString = React.createContext("lol"); @@ -30,13 +31,13 @@ const builder =(inState:Store):Store=> return inState; } + export default ()=> { - const [Store, Dispatch] = React.useReducer(reducer, {name:"seth", age:24} as Store, builder) return
-

Title!!!!

+

Title?

subtitle!

@@ -48,5 +49,5 @@ export default ()=> - -} \ No newline at end of file + ; +} diff --git a/example/deno.json b/example/deno.json index cef02df..e10cb86 100644 --- a/example/deno.json +++ b/example/deno.json @@ -2,12 +2,11 @@ "compilerOptions": { "lib": ["deno.window", "dom"] }, "imports": { - "react":"https://esm.sh/preact@10.15.1/compat", - "@app": "./app.tsx" + "react":"https://esm.sh/preact@10.15.1/compat" }, "tasks": { - "local": "deno run -A --no-lock ../local.tsx", - "serve": "deno run -A --no-lock ../serve.tsx" + "local": "deno run -A --no-lock app.tsx --dev", + "serve": "deno run -A --no-lock app.tsx" } } \ No newline at end of file diff --git a/local.tsx b/local.tsx index aee018c..055c59b 100644 --- a/local.tsx +++ b/local.tsx @@ -2,10 +2,8 @@ import {Configure, Transpile, Extension} from "./serve.tsx"; const SocketsLive:Set = new Set(); const SocketsSend =(inData:string)=>{ console.log(inData); for (const socket of SocketsLive){ socket.send(inData); } } -const Directory = `file://${Deno.cwd().replaceAll("\\", "/")}`; Configure({ - Proxy:Directory, SWCOp: { sourceMaps: "inline", @@ -36,11 +34,11 @@ Configure({ console.log(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_/")) { - const imp = await import(Directory+inURL.pathname); + const imp = await import(inProxy+inURL.pathname); const members = []; for( const key in imp ) { members.push(key); } @@ -73,35 +71,40 @@ FileListen("${inURL.pathname}", (updatedModule)=> } }); -let blocking = false; -const filesChanged:Map = new Map(); -for await (const event of Deno.watchFs(Deno.cwd())) +const Watcher =async()=> { - event.paths.forEach( path => filesChanged.set(path, event.kind) ); - if(!blocking) + let blocking = false; + const filesChanged:Map = new Map(); + for await (const event of Deno.watchFs(Deno.cwd())) { - blocking = true; - setTimeout(async()=> + event.paths.forEach( path => filesChanged.set(path, event.kind) ); + 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(action != "remove") - { - const tsx = await Transpile.Fetch(Directory+key, key, true); - tsx && SocketsSend(key); - } - else + if(Transpile.Check(Extension(path))) { - Transpile.Cache.delete(key); + const key = path.substring(Deno.cwd().length).replaceAll("\\", "/"); + if(action != "remove") + { + const tsx = await Transpile.Fetch(`file://${Deno.cwd().replaceAll("\\", "/")}`+key, key, true); + tsx && SocketsSend(key); + } + else + { + Transpile.Cache.delete(key); + } } } + filesChanged.clear(); + blocking = false; } - filesChanged.clear(); - blocking = false; + , 1000); } - , 1000); } -} \ No newline at end of file +} + +Watcher().then(()=>console.log("done watching")); \ No newline at end of file diff --git a/serve.tsx b/serve.tsx index a0a7dd1..c3f06a0 100644 --- a/serve.tsx +++ b/serve.tsx @@ -23,16 +23,16 @@ const ImportMapReload =async()=> ImportMap.imports = Configuration.Remap(json.imports); }; -type CustomHTTPHandler = (inReq:Request, inURL:URL, inExt:string|false, inMap:{imports:Record})=>void|false|Response|Promise; +type CustomHTTPHandler = (inReq:Request, inURL:URL, inExt:string|false, inMap:{imports:Record}, inProxy:string)=>void|false|Response|Promise; type CustomRemapper = (inImports:Record)=>Record; 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}; let Configuration:Configuration = { - Proxy: `file://${Deno.cwd().replaceAll("\\", "/")}`, + Proxy: new URL(`file://${Deno.cwd().replaceAll("\\", "/")}`).toString(), Allow: "*", Reset: "/clear-cache", - Serve(inReq, inURL, inExt, inMap){}, + Serve(inReq, inURL, inExt, inMap, inProxy){}, Remap: (inImports)=> { Object.entries(inImports).forEach(([key, value])=> @@ -51,8 +51,13 @@ let Configuration:Configuration = console.log(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( ` @@ -62,8 +67,8 @@ let Configuration:Configuration =

`, {status:200, headers:{"content-type":"text/html"}}); @@ -162,7 +167,7 @@ HTTP.serve(async(req: Request)=> } // 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) { return custom; @@ -171,26 +176,36 @@ HTTP.serve(async(req: Request)=> // transpileable files if(Transpile.Check(ext)) { + let code; + let path; if(url.pathname.startsWith("/_lib_/")) { - const path = import.meta.url+"/.."+url.pathname; - const code = await Transpile.Fetch(path, url.pathname, true); - if(code) + if(url.pathname.endsWith("boot.tsx")) { - 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 { - const lookup = await Transpile.Fetch(Configuration.Proxy + url.pathname, url.pathname); - return new Response(lookup, {status:lookup?200:404, headers:{...headers, "content-type":"application/javascript"}} ); + path = Configuration.Proxy + url.pathname; + code = await Transpile.Fetch(path, url.pathname); } + + if(code) + { + return new Response(code, {headers:{...headers, "content-type":"application/javascript"}} ); + } } // custom page html if(!ext) { - const shell = await Configuration.Shell(req, url, ext, ImportMap); + const shell = await Configuration.Shell(req, url, ext, ImportMap, Configuration.Proxy); if(shell) { return shell;