From 70a0c2e96f2a74742e8220100b42b35a1f243485 Mon Sep 17 00:00:00 2001 From: Seth Trowbridge Date: Sun, 30 Apr 2023 15:11:54 -0400 Subject: [PATCH] split boot option --- circ/b.tsx | 12 ++ example/app.tsx | 64 ++++---- lib/boot-client.tsx | 30 ++++ lib/iso.tsx | 16 +- server.tsx | 347 +++++++++++++++++++++----------------------- 5 files changed, 246 insertions(+), 223 deletions(-) create mode 100644 lib/boot-client.tsx diff --git a/circ/b.tsx b/circ/b.tsx index a9b326c..be8d563 100644 --- a/circ/b.tsx +++ b/circ/b.tsx @@ -1,8 +1,20 @@ +import * as mod from "https://deno.land/std@0.185.0/path/mod.ts"; import { doSomethingElse } from './a.tsx'; export function doSomething() { + const pathInit = Deno.mainModule; + + const pathProj = mod.toFileUrl(Deno.cwd()); + + console.log(pathInit); + console.log(import.meta.url); + + console.log(pathInit.split(pathProj)); + console.log("b main?", Deno.mainModule); + console.log("deno main?", Deno.cwd()); + Deno.env.set("b", "called"); console.log("b action"); diff --git a/example/app.tsx b/example/app.tsx index 8a7cd9e..da58b1d 100644 --- a/example/app.tsx +++ b/example/app.tsx @@ -3,34 +3,36 @@ import * as Iso from "@eno/iso"; const Comp = React.lazy(()=>import("./deep/component.tsx")); -export default ()=> -{ - return
- - - -

Title!!

-

suspended:

- Loading!
}> - - - - - - - <> - - About us! - - - sorry no page - - - lol/idk -

404!

-
- ; -}; \ No newline at end of file +Iso.Boot( + ()=> + { + return
+ + + +

Title!!!!!!

+

suspended:

+ Loading!
}> + + + + + + + <> + + About us! + + + sorry no page + + + lol/idk +

404!

+
+ ; + } +); \ No newline at end of file diff --git a/lib/boot-client.tsx b/lib/boot-client.tsx new file mode 100644 index 0000000..6eb1f15 --- /dev/null +++ b/lib/boot-client.tsx @@ -0,0 +1,30 @@ +import React, {hydrate} from "react"; +import * as Twind from "https://esm.sh/v115/@twind/core@1.1.3/es2022/core.mjs"; +import {Router, CSS, Meta} from "@eno/iso"; + +export function Boot(inApp:()=>React.JSX.Element, inCSS?:object) +{ + console.log(inApp, inCSS); + Twind.install(inCSS ? {...CSS, ...inCSS} : CSS); + + const HMRWrap =()=> React.createElement(inApp, null, null); + + const root = document.querySelector("#app"); + + if(root) + { + hydrate( + + + + + , + root + ); + } + else + { + console.log(`no "#app" element is present!`) + } + +}; \ No newline at end of file diff --git a/lib/iso.tsx b/lib/iso.tsx index c10e021..231e35e 100644 --- a/lib/iso.tsx +++ b/lib/iso.tsx @@ -1,15 +1,7 @@ import TWPreTail from "https://esm.sh/v115/@twind/preset-tailwind@1.1.4/es2022/preset-tailwind.mjs"; import TWPreAuto from "https://esm.sh/v115/@twind/preset-autoprefix@1.0.7/es2022/preset-autoprefix.mjs"; import React from "react"; - -import {INIT} from "../server.tsx"; - -function wrapper() -{ - INIT(); -} - -wrapper(); +import { Boot as _Boot } from "../server.tsx"; export const CSS = { presets: [TWPreTail(), TWPreAuto()], @@ -22,6 +14,12 @@ if(!window.innerWidth) } */ +export function Boot(inApp:React.FunctionComponent, inCSS?:object) +{ + _Boot(inApp, inCSS); +} + + type MetasInputs = { [Property in MetaKeys]?: string }; type MetasModeArgs = {concatListed?:boolean; dropUnlisted?:boolean}; type MetasStackItem = MetasModeArgs&MetasInputs&{id:string, depth:number} diff --git a/server.tsx b/server.tsx index f36f489..49bccdb 100644 --- a/server.tsx +++ b/server.tsx @@ -2,16 +2,13 @@ import * as ESBuild from 'https://deno.land/x/esbuild@v0.17.4/mod.js'; import * as MIME from "https://deno.land/std@0.180.0/media_types/mod.ts"; import { debounce } from "https://deno.land/std@0.151.0/async/debounce.ts"; import { parse as JSONC} from "https://deno.land/std@0.185.0/jsonc/mod.ts"; +import { toFileUrl } from "https://deno.land/std@0.185.0/path/mod.ts"; import SSR from "https://esm.sh/v113/preact-render-to-string@6.0.2"; import Prepass from "https://esm.sh/preact-ssr-prepass@1.2.0"; import * as Twind from "https://esm.sh/@twind/core@1.1.3"; import React from "react"; import * as Iso from "@eno/iso"; -Deno.env.set("initialized", "true"); - -export const INIT =()=> console.log("init!"); - /** * Setup a transpiler. * @param inDevMode When true, starts a file-watcher @@ -136,6 +133,7 @@ function Transpiler(inDevMode:boolean) } +type ImportMap = {imports?:Record, importMap?:string}; /** * Extract all configuration info form a project's deno.jsonc file * @param inDevMode When true, proxies react to an HMR-enabled version @@ -144,8 +142,7 @@ function Transpiler(inDevMode:boolean) */ async function Configure(inDevMode:boolean, inLibPath:string) { - type ImportMap = {imports?:Record, importMap?:string}; - const output:{Imports?:ImportMap, App?:React.FunctionComponent, TwindInst?:Twind.Twind, Error?:string} = {}; + const output:{Imports?:ImportMap, Error?:string} = {}; let ImportObject:ImportMap = {}; try { @@ -206,52 +203,6 @@ async function Configure(inDevMode:boolean, inLibPath:string) return output; } - const importApp = output.Imports.imports["@eno/app"]; - if(importApp) - { - let appImport - try - { - appImport = await import(Path.Active+importApp); - } - catch(e) - { - output.Error = `"@eno/app" entry-point (${importApp}) file not found`; - return output; - } - - if(typeof appImport.default == "function" ) - { - output.App = appImport.default; - } - else - { - output.Error = `"@eno/app" entry-point (${importApp}) needs to export a default function to use as the app root.`; - return output; - } - - let twindConfig = Iso.CSS; - if(typeof appImport.CSS == "object") - { - twindConfig = {...twindConfig, ...appImport.CSS}; - } - try - { - // @ts-ignore - output.TwindInst = Twind.install(twindConfig); - } - catch(e) - { - output.Error = `CSS configuration is malformed`; - return output; - } - } - else - { - output.Error = `"imports" configuration does not alias an entry-point file as "@eno/app"`; - return output; - } - Object.entries(output.Imports.imports).forEach(([key, value])=>{ if(value.startsWith("./") && output.Imports?.imports) @@ -274,166 +225,196 @@ async function Configure(inDevMode:boolean, inLibPath:string) return output; } -const Flags:Record = {}; Deno.args.forEach(arg=> { if(arg.startsWith("--")) { const kvp = arg.substring(2).split("="); - Flags[kvp[0]] = kvp[1] ? kvp[1] : true; + Deno.env.set(kvp[0], kvp[1] ? kvp[1] : "true"); } }); -let Booted = false; -export function Boot(inApp:React.JSX.Element, inCSS?:object) -{ - if(Booted){return;} - Booted = true; - -} - -let DevMode = Flags.dev ? true : false; +let DevMode = Deno.env.get("dev") ? true : false; let hosted = import.meta.resolve("./"); const Path = { Hosted: hosted.substring(0, hosted.length-1), Active: `file://${Deno.cwd().replaceAll("\\", "/")}`, - LibDir: "lib" + LibDir: "lib", + AppDir: "" }; console.log(Path); console.log(`Dev Mode: ${DevMode}`); -console.log(`Args seen: `, Flags); +console.log(`Args seen: `, Deno.env.toObject); -const {Transpileable, TranspileURL, SocketsHandler} = Transpiler(DevMode); -const {Imports, App, TwindInst, Error} = await Configure(DevMode, Path.LibDir); -if(Error) + +let Booted = false; +let TwindInst:Twind.Twind; +export function Boot(inApp:React.FunctionComponent, inCSS?:object) { - console.log(Error); + if(Booted){return;} + Booted = true; + + const pathInit = Deno.mainModule; + const pathProj = toFileUrl(Deno.cwd()); + + //@ts-ignore + TwindInst = Twind.install({...Iso.CSS, ...inCSS||{}}); + + const App = inApp; + Path.AppDir = pathInit.split(pathProj.toString())[1]; + + Server(App, Path.AppDir, TwindInst); } -else if(App && TwindInst) + +async function Server(App:React.FunctionComponent, AppPath:string, TwindInst:Twind.Twind) { - Deno.serve({ port: Flags?.port||3000 }, async(_req:Request) => + const {Transpileable, TranspileURL, SocketsHandler} = Transpiler(DevMode); + const {Imports, Error} = await Configure(DevMode, Path.LibDir); + if(Error) { - const url:URL = new URL(_req.url); - const pathParts = url.pathname.substring(1, url.pathname.endsWith("/") ? url.pathname.length-1 : url.pathname.length).split("/"); - const pathLast = pathParts.at(-1); - const pathExt:string|undefined = pathLast?.split(".")[1]; - - const resp = SocketsHandler(_req); - if(resp){ return resp; } - - try + console.log(Error); + } + else if(App && TwindInst) + { + Deno.serve({ port: Deno.env.get("port")||3000 }, async(_req:Request) => { - // serve index by default - let type = `text/html`; - let body:BodyInit = ``; - - const isLib = url.pathname.startsWith(`/${Path.LibDir}/`); - - if(Transpileable(url.pathname)) + const url:URL = new URL(_req.url); + const pathParts = url.pathname.substring(1, url.pathname.endsWith("/") ? url.pathname.length-1 : url.pathname.length).split("/"); + const pathLast = pathParts.at(-1); + const pathExt:string|undefined = pathLast?.split(".")[1]; + + const resp = SocketsHandler(_req); + if(resp){ return resp; } + + console.log(url.pathname); + + try { - type = `application/javascript`; - if(isLib) + // serve index by default + let type = `text/html`; + let body:BodyInit = ``; + + const isLib = url.pathname.startsWith(`/${Path.LibDir}/`); + + if(Transpileable(url.pathname)) { - body = await TranspileURL(Path.Hosted+url.pathname, url.pathname, true); + type = `application/javascript`; + if(isLib) + { + body = await TranspileURL(Path.Hosted+url.pathname, url.pathname, true); + } + else if(url.pathname == "/server.tsx") + { +body = ` + +import {hydrate, createElement as H} from "react"; +import * as Twind from "https://esm.sh/v115/@twind/core@1.1.3/es2022/core.mjs"; +import {Router, CSS, Meta} from "@eno/iso"; +export function Boot(inApp, inCSS) +{ + console.log(inApp, inCSS); + Twind.install(inCSS ? {...CSS, ...inCSS} : CSS); + + const hmrWrap = H( ()=>H(inApp) ); + hydrate( + H(Router.Provider, {url:window.location}, + H(Meta.Provider, null, hmrWrap) + ), + document.querySelector("#app") + ); + +}`; + } + else if(DevMode && !url.searchParams.get("reload")) + { + const imp = await import(Path.Active+url.pathname); + const members = []; + for( const key in imp ) { members.push(key); } + body = + ` + import {FileListen} from "/${Path.LibDir}/hmr.tsx"; + import * as Import from "${url.pathname}?reload=0"; + ${ members.map(m=>`let proxy_${m} = Import.${m}; + export { proxy_${m} as ${m} }; + `).join(" ") } + const reloadHandler = (updatedModule)=> + { + ${ members.map(m=>`proxy_${m} = updatedModule.${m};`).join("\n") } + }; + FileListen("${url.pathname}", reloadHandler);`; + + } + else + { + body = await TranspileURL(Path.Active+url.pathname, url.pathname, true); + } } - else if(DevMode && !url.searchParams.get("reload")) + // serve static media + else if( pathExt ) { - const imp = await import(Path.Active+url.pathname); - const members = []; - for( const key in imp ) { members.push(key); } - body = - ` - import {FileListen} from "/${Path.LibDir}/hmr.tsx"; - import * as Import from "${url.pathname}?reload=0"; - ${ members.map(m=>`let proxy_${m} = Import.${m}; - export { proxy_${m} as ${m} }; - `).join(" ") } - const reloadHandler = (updatedModule)=> - { - ${ members.map(m=>`proxy_${m} = updatedModule.${m};`).join("\n") } - }; - FileListen("${url.pathname}", reloadHandler);`; - + type = MIME.typeByExtension(pathExt) || "text/html"; + const _fetch = await fetch((Path.Active)+url.pathname); + body = await _fetch.text(); } else { - body = await TranspileURL(Path.Active+url.pathname, url.pathname, true); - } - } - // serve static media - else if( pathExt ) - { - type = MIME.typeByExtension(pathExt) || "text/html"; - const _fetch = await fetch((Path.Active)+url.pathname); - body = await _fetch.text(); - } - else - { - Iso.Fetch.ServerBlocking = []; - Iso.Fetch.ServerTouched = new Set(); - Iso.Fetch.ServerRemove = new Set(); - let app = ; - await Prepass(app) - let bake = SSR(app); - while(Iso.Fetch.ServerBlocking.length) - { - await Promise.all(Iso.Fetch.ServerBlocking); Iso.Fetch.ServerBlocking = []; - // at this point, anything that was requested that was not cached, has now been loaded and cached - // this next render will use cached resources. using a cached resource (if its "Seed" is true) adds it to the "touched" set. - app = ; + Iso.Fetch.ServerTouched = new Set(); + Iso.Fetch.ServerRemove = new Set(); + let app = ; await Prepass(app) - bake = SSR(app); + let bake = SSR(app); + while(Iso.Fetch.ServerBlocking.length) + { + await Promise.all(Iso.Fetch.ServerBlocking); + Iso.Fetch.ServerBlocking = []; + // at this point, anything that was requested that was not cached, has now been loaded and cached + // this next render will use cached resources. using a cached resource (if its "Seed" is true) adds it to the "touched" set. + app = ; + await Prepass(app) + bake = SSR(app); + } + + const seed:Iso.FetchRecord[] = []; + Iso.Fetch.ServerTouched.forEach((record)=>{ + const r:Iso.FetchRecord = {...record}; + delete r.Promise; + seed.push(r); + }); + Iso.Fetch.ServerTouched = false; + + const results = Twind.extract(bake, TwindInst); + + type = `text/html`; + body = + ` + + + ${Iso.Meta.Meta.title} + + + + + + +
${results.html}
+ + + `; } - - const seed:Iso.FetchRecord[] = []; - Iso.Fetch.ServerTouched.forEach((record)=>{ - const r:Iso.FetchRecord = {...record}; - delete r.Promise; - seed.push(r); - }); - Iso.Fetch.ServerTouched = false; - - const results = Twind.extract(bake, TwindInst); - - type = `text/html`; - body = - ` - - - ${Iso.Meta.Meta.title} - - - - - - -
${results.html}
- - - `; + + return new Response(body, {headers:{"content-type":type as string, "Access-Control-Allow-Origin":"*", charset:"utf-8"}}); } - - return new Response(body, {headers:{"content-type":type as string, "Access-Control-Allow-Origin":"*", charset:"utf-8"}}); - } - catch(error) - { - console.log(error); - return new Response(error, {status:404}); - } - }); -} \ No newline at end of file + catch(error) + { + console.log(error); + return new Response(error, {status:404}); + } + }); + } +}