diff --git a/example/app.tsx b/example/app.tsx index ea4529d..c785b1f 100644 --- a/example/app.tsx +++ b/example/app.tsx @@ -1,16 +1,19 @@ 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 Component from "./deep/component.tsx"; import * as Iso from "@eno/iso"; +const Comp = React.lazy(()=>import("./deep/component.tsx")); + export default ()=> { return

Title!!

-

subtitle

- +

suspended:

+ Loading!
}> + + diff --git a/example/deep/component.tsx b/example/deep/component.tsx index f588cf3..a93ae9a 100644 --- a/example/deep/component.tsx +++ b/example/deep/component.tsx @@ -7,13 +7,15 @@ export default ()=> const [routeGet, routeSet] = Iso.Router.Consumer(); type CatFact = {fact:string, length:number}|undefined; - const [Data, Updating] = Iso.Fetch.Use(`https://catfact.ninja/fact`); + const [Data, Updating] = Iso.Fetch.Use(`https://catfact.ninja/fact`, undefined, 60, true, true, true); + + + console.log("render!!") return
- Route is: {routeGet.Path.toString()} + Component Route is: {routeGet.Path.toString()} - Component!!! a link

Data:{Data && (Data as CatFact)?.fact}

Status:{Updating?'loading':'done'}

diff --git a/lib/iso.tsx b/lib/iso.tsx index 071cbd7..2989821 100644 --- a/lib/iso.tsx +++ b/lib/iso.tsx @@ -161,6 +161,8 @@ export const Case =({children, value}:{children:Children, value?:string, default export const useRouteVars =()=> React.useContext(SwitchContext).keys; export type FetchRecord = {URL:string, CacheFor:number, CachedAt:number, CacheOnServer:boolean, Promise?:Promise, DelaySSR:boolean, Seed:boolean, Error?:string, JSON?:object}; +type FetchGuide = [Record:FetchRecord, Init:boolean, Listen:boolean]; +export type FetchHookState = [Data:undefined|object, Updating:boolean]; export const Fetch = { Cache:new Map() as Map, ServerBlocking:false as false|Promise[], @@ -169,11 +171,11 @@ export const Fetch = { Seed(seed:FetchRecord[]) { seed.forEach(r=>{ - r.Promise = Promise.resolve(r); + //r.Promise = Promise.resolve(r); Fetch.Cache.set(r.URL, r) }); }, - Request(URL:string, Init?:RequestInit, CacheFor:number = 60, CacheOnServer:boolean = true, DelaySSR:boolean = true, Seed:boolean = true) + Request(URL:string, Init?:RequestInit, CacheFor:number = 60, CacheOnServer:boolean = true, DelaySSR:boolean = true, Seed:boolean = true):FetchGuide { let check = Fetch.Cache.get(URL); @@ -188,17 +190,24 @@ export const Fetch = { return inCheck; }); return inCheck; - } + }; if(!check) { + // not in the cache + // - listen + console.log(`making new cache record...`); - return load({URL, CacheFor, CachedAt:0, CacheOnServer, DelaySSR, Seed}); + return [load({URL, CacheFor, CachedAt:0, CacheOnServer, DelaySSR, Seed}), false, true]; } else if(check.CachedAt == 0) { + // loading started but not finished + // - listen + // - possibly init if there is something in JSON + console.log(`currently being cached...`); - return check; + return [check, check.JSON ? true : false, true]; } else { @@ -206,38 +215,49 @@ export const Fetch = { let secondsAge = (new Date().getTime() - check.CachedAt)/1000; if(secondsAge > check.CacheFor) { + // cached but expired + // - listen + // - init console.log(`...outdated...`); - return load(check); + return [load(check), true, true]; } else { + // cached and ready + // - init console.log(`...retrieved!`); - return check; + return [check, true, false]; } } }, Use(URL:string, Init?:RequestInit, CacheFor:number = 60, CacheOnServer:boolean = true, DelaySSR:boolean = true, Seed:boolean = true) { - type FetchHookState = [Data:undefined|object, Updating:boolean]; - const [cacheGet, cacheSet] = React.useState([undefined, true] as FetchHookState); - if(Fetch.ServerBlocking && Fetch.ServerTouched && DelaySSR) + const [receipt, init, listen] = Fetch.Request(URL, Init, CacheFor, CacheOnServer, DelaySSR, Seed); + const initialState:FetchHookState = init ? [receipt.JSON, listen] : [undefined, true]; + const [cacheGet, cacheSet] = React.useState(initialState); + + if(Fetch.ServerBlocking && Fetch.ServerTouched && DelaySSR) // if server-side rendering { - const receipt = Fetch.Request(URL, Init, CacheFor, CacheOnServer, DelaySSR, Seed); - if(receipt.CachedAt === 0 && receipt.Promise)// request for something that hasnt been cached yet + if(listen) // if the request is pending { - Fetch.ServerBlocking.push(receipt.Promise); + receipt.Promise && Fetch.ServerBlocking.push(receipt.Promise); // add promise to blocking list + return [undefined, listen] as FetchHookState; // no need to return any actual data while waiting server-side } - else if(receipt.Seed) + else // if request is ready { - Fetch.ServerTouched.add(receipt); + receipt.Seed && Fetch.ServerTouched.add(receipt); // add record to client seed list (if specified in receipt.seed) + return [receipt.JSON, false] as FetchHookState; } - return [receipt.JSON, receipt.CachedAt === 0]; } + React.useEffect(()=> { - const receipt = Fetch.Request(URL, Init, CacheFor, CacheOnServer, DelaySSR); - receipt.Promise?.then(()=>cacheSet([receipt.JSON, receipt.CachedAt === 0])); + if(listen) + { + //const receipt = Fetch.Request(URL, Init, CacheFor, CacheOnServer, DelaySSR); + receipt.Promise?.then(()=>cacheSet([receipt.JSON, receipt.CachedAt === 0])); + } } , []); diff --git a/server.tsx b/server.tsx index 2aae3d6..7074a81 100644 --- a/server.tsx +++ b/server.tsx @@ -2,6 +2,7 @@ import * as ESBuild from 'https://deno.land/x/esbuild@v0.14.45/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 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"; @@ -35,7 +36,8 @@ const Transpileable =(inFilePath:string):boolean=> }; const Transpile =async(inCode:string, inKey:string):Promise=> { - const transpile = await ESBuild.transform(inCode, { loader: "tsx", minify:true}); + const transpile = await ESBuild.transform(inCode, { loader: "tsx", sourcemap:"inline", minify:true, sourcefile:inKey}); + Transpiled.set(inKey, transpile.code); return transpile.code; }; @@ -224,14 +226,18 @@ FileListen("${url.pathname}", reloadHandler);`; Iso.Fetch.ServerBlocking = []; Iso.Fetch.ServerTouched = new Set(); Iso.Fetch.ServerRemove = new Set(); - let bake = SSR(); + 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. - bake = SSR(); + app = ; + await Prepass(app) + bake = SSR(app); } const seed:Iso.FetchRecord[] = []; @@ -258,13 +264,13 @@ FileListen("${url.pathname}", reloadHandler);`;
${results.html}
`;