Compare commits

..

No commits in common. "63137a7726c4b00a6be5666dff64e6340f00e963" and "26cb46923fead330d205950a7b3ac06707c159e7" have entirely different histories.

4 changed files with 34 additions and 69 deletions

View File

@ -1,19 +1,16 @@
import TWPreTail from "https://esm.sh/v115/@twind/preset-tailwind@1.1.4/es2022/preset-tailwind.mjs"; 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 TWPreAuto from "https://esm.sh/v115/@twind/preset-autoprefix@1.0.7/es2022/preset-autoprefix.mjs";
import React from "react"; import React from "react";
import Component from "./deep/component.tsx";
import * as Iso from "@eno/iso"; import * as Iso from "@eno/iso";
const Comp = React.lazy(()=>import("./deep/component.tsx"));
export default ()=> export default ()=>
{ {
return <div class="p-4 font-sans"> return <div class="p-4 font-sans">
<Iso.Metas title="Main Page!"/> <Iso.Metas title="Main Page!"/>
<h1 class="my-2 font(bold serif) text(2xl)">Title!!</h1> <h1 class="my-2 font(bold serif) text(2xl)">Title!!</h1>
<h2>suspended:</h2> <h2>subtitle</h2>
<React.Suspense fallback={<div>Loading!</div>}> <Component/>
<Comp/>
</React.Suspense>
<Iso.Switch> <Iso.Switch>
<Iso.Case value="page"> <Iso.Case value="page">
<Iso.Switch> <Iso.Switch>

View File

@ -7,15 +7,13 @@ export default ()=>
const [routeGet, routeSet] = Iso.Router.Consumer(); const [routeGet, routeSet] = Iso.Router.Consumer();
type CatFact = {fact:string, length:number}|undefined; type CatFact = {fact:string, length:number}|undefined;
const [Data, Updating] = Iso.Fetch.Use(`https://catfact.ninja/fact`, undefined, 60, true, true, true); const [Data, Updating] = Iso.Fetch.Use(`https://catfact.ninja/fact`);
console.log("render!!")
return <div class="p-4 text-red-500"> return <div class="p-4 text-red-500">
<Iso.Metas title="Component!"/> <Iso.Metas title="Component!"/>
Component Route is: {routeGet.Path.toString()} Route is: {routeGet.Path.toString()}
<button className="p-4 bg-red-500 text-white" onClick={e=>{countSet(countGet+1); routeSet(["lol", "idk"], {count:countGet+1});}}>{countGet}</button> <button className="p-4 bg-red-500 text-white" onClick={e=>{countSet(countGet+1); routeSet(["lol", "idk"], {count:countGet+1});}}>{countGet}</button>
Component!!!
<a href="/page/about-us" className="p-2 text(lg blue-500) font-bold">a link</a> <a href="/page/about-us" className="p-2 text(lg blue-500) font-bold">a link</a>
<p>Data:{Data && (Data as CatFact)?.fact}</p> <p>Data:{Data && (Data as CatFact)?.fact}</p>
<p>Status:{Updating?'loading':'done'}</p> <p>Status:{Updating?'loading':'done'}</p>

View File

@ -160,22 +160,19 @@ export const Switch =({children}:{children:Children})=>
export const Case =({children, value}:{children:Children, value?:string, default?:true})=>null; export const Case =({children, value}:{children:Children, value?:string, default?:true})=>null;
export const useRouteVars =()=> React.useContext(SwitchContext).keys; export const useRouteVars =()=> React.useContext(SwitchContext).keys;
export type FetchRecord = {URL:string, CacheFor:number, CachedAt:number, CacheOnServer:boolean, Promise?:Promise<FetchRecord>, DelaySSR:boolean, Seed:boolean, Error?:string, JSON?:object}; export type FetchRecord = {URL:string, CacheFor:number, CachedAt:number, CacheOnServer:boolean, Promise?:Promise<FetchRecord>, DelaySSR:boolean, Error?:string, JSON?:object};
type FetchGuide = [Record:FetchRecord, Init:boolean, Listen:boolean];
export type FetchHookState = [Data:undefined|object, Updating:boolean];
export const Fetch = { export const Fetch = {
Cache:new Map() as Map<string, FetchRecord>, Cache:new Map() as Map<string, FetchRecord>,
ServerBlocking:false as false|Promise<FetchRecord>[], ServerBlocking:false as false|Promise<FetchRecord>[],
ServerTouched:false as false|Set<FetchRecord>, ServerTouched:new Set() as Set<FetchRecord>,
ServerRemove:false as false|Set<FetchRecord>,
Seed(seed:FetchRecord[]) Seed(seed:FetchRecord[])
{ {
seed.forEach(r=>{ seed.forEach(r=>{
//r.Promise = Promise.resolve(r); r.Promise = Promise.resolve(r);
Fetch.Cache.set(r.URL, r) Fetch.Cache.set(r.URL, r)
}); });
}, },
Request(URL:string, Init?:RequestInit, CacheFor:number = 60, CacheOnServer:boolean = true, DelaySSR:boolean = true, Seed:boolean = true):FetchGuide Request(URL:string, Init?:RequestInit, CacheFor:number = 60, CacheOnServer:boolean = true, DelaySSR:boolean = true)
{ {
let check = Fetch.Cache.get(URL); let check = Fetch.Cache.get(URL);
@ -190,24 +187,17 @@ export const Fetch = {
return inCheck; return inCheck;
}); });
return inCheck; return inCheck;
}; }
if(!check) if(!check)
{ {
// not in the cache
// - listen
console.log(`making new cache record...`); console.log(`making new cache record...`);
return [load({URL, CacheFor, CachedAt:0, CacheOnServer, DelaySSR, Seed}), false, true]; return load({URL, CacheFor, CachedAt:0, CacheOnServer, DelaySSR});
} }
else if(check.CachedAt == 0) else if(check.CachedAt == 0)
{ {
// loading started but not finished
// - listen
// - possibly init if there is something in JSON
console.log(`currently being cached...`); console.log(`currently being cached...`);
return [check, check.JSON ? true : false, true]; return check;
} }
else else
{ {
@ -215,50 +205,39 @@ export const Fetch = {
let secondsAge = (new Date().getTime() - check.CachedAt)/1000; let secondsAge = (new Date().getTime() - check.CachedAt)/1000;
if(secondsAge > check.CacheFor) if(secondsAge > check.CacheFor)
{ {
// cached but expired
// - listen
// - init
console.log(`...outdated...`); console.log(`...outdated...`);
return [load(check), true, true]; return load(check);
} }
else else
{ {
// cached and ready
// - init
console.log(`...retrieved!`); console.log(`...retrieved!`);
return [check, true, false]; return check;
} }
} }
}, },
Use(URL:string, Init?:RequestInit, CacheFor:number = 60, CacheOnServer:boolean = true, DelaySSR:boolean = true, Seed:boolean = true) Use(URL:string, Init?:RequestInit, CacheFor:number = 60, CacheOnServer:boolean = true, DelaySSR:boolean = true)
{ {
const [receipt, init, listen] = Fetch.Request(URL, Init, CacheFor, CacheOnServer, DelaySSR, Seed); type FetchHookState = [Data:undefined|object, Updating:boolean];
const initialState:FetchHookState = init ? [receipt.JSON, listen] : [undefined, true]; const [cacheGet, cacheSet] = React.useState([undefined, true] as FetchHookState);
const [cacheGet, cacheSet] = React.useState(initialState); if(Fetch.ServerBlocking && DelaySSR)
if(Fetch.ServerBlocking && Fetch.ServerTouched && DelaySSR) // if server-side rendering
{ {
if(listen) // if the request is pending const receipt = Fetch.Request(URL, Init, CacheFor, CacheOnServer, DelaySSR);
if(receipt.CachedAt === 0 && receipt.Promise)
{ {
receipt.Promise && Fetch.ServerBlocking.push(receipt.Promise); // add promise to blocking list Fetch.ServerBlocking.push(receipt.Promise);
return [undefined, listen] as FetchHookState; // no need to return any actual data while waiting server-side
} }
else // if request is ready else
{ {
receipt.Seed && Fetch.ServerTouched.add(receipt); // add record to client seed list (if specified in receipt.seed) Fetch.ServerTouched.add(receipt);
return [receipt.JSON, false] as FetchHookState;
} }
return [receipt.JSON, receipt.CachedAt === 0];
} }
React.useEffect(()=> React.useEffect(()=>
{ {
if(listen) const receipt = Fetch.Request(URL, Init, CacheFor, CacheOnServer, DelaySSR);
{
//const receipt = Fetch.Request(URL, Init, CacheFor, CacheOnServer, DelaySSR);
receipt.Promise?.then(()=>cacheSet([receipt.JSON, receipt.CachedAt === 0])); receipt.Promise?.then(()=>cacheSet([receipt.JSON, receipt.CachedAt === 0]));
} }
}
, []); , []);
return cacheGet; return cacheGet;

View File

@ -2,7 +2,6 @@ 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 * 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 { 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 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 * as Twind from "https://esm.sh/@twind/core@1.1.3";
import React from "react"; import React from "react";
import * as Iso from "@eno/iso"; import * as Iso from "@eno/iso";
@ -36,8 +35,7 @@ const Transpileable =(inFilePath:string):boolean=>
}; };
const Transpile =async(inCode:string, inKey:string):Promise<string>=> const Transpile =async(inCode:string, inKey:string):Promise<string>=>
{ {
const transpile = await ESBuild.transform(inCode, { loader: "tsx", sourcemap:"inline", minify:true, sourcefile:inKey}); const transpile = await ESBuild.transform(inCode, { loader: "tsx", minify:true});
Transpiled.set(inKey, transpile.code); Transpiled.set(inKey, transpile.code);
return transpile.code; return transpile.code;
}; };
@ -224,29 +222,22 @@ FileListen("${url.pathname}", reloadHandler);`;
else else
{ {
Iso.Fetch.ServerBlocking = []; Iso.Fetch.ServerBlocking = [];
Iso.Fetch.ServerTouched = new Set(); let bake = SSR(<Iso.Router.Provider url={url}><App/></Iso.Router.Provider>);
Iso.Fetch.ServerRemove = new Set();
let app = <Iso.Router.Provider url={url}><App/></Iso.Router.Provider>;
await Prepass(app)
let bake = SSR(app);
while(Iso.Fetch.ServerBlocking.length) while(Iso.Fetch.ServerBlocking.length)
{ {
await Promise.all(Iso.Fetch.ServerBlocking); await Promise.all(Iso.Fetch.ServerBlocking);
Iso.Fetch.ServerBlocking = []; Iso.Fetch.ServerBlocking = [];
// at this point, anything that was requested that was not cached, has now been loaded and cached bake = SSR(<Iso.Router.Provider url={url}><App/></Iso.Router.Provider>);
// this next render will use cached resources. using a cached resource (if its "Seed" is true) adds it to the "touched" set.
app = <Iso.Router.Provider url={url}><App/></Iso.Router.Provider>;
await Prepass(app)
bake = SSR(app);
} }
const seed:Iso.FetchRecord[] = []; const seed:Iso.FetchRecord[] = [];
Iso.Fetch.ServerTouched.forEach((record)=>{ Iso.Fetch.ServerTouched.forEach((record)=>{
const r:Iso.FetchRecord = {...record}; const r:Iso.FetchRecord = {...record};
delete r.Promise; delete r.Promise;
seed.push(r); seed.push(r);
}); });
Iso.Fetch.ServerTouched = false; Iso.Fetch.ServerTouched = new Set();
const results = Twind.extract(bake, TwindInst); const results = Twind.extract(bake, TwindInst);
@ -264,13 +255,13 @@ FileListen("${url.pathname}", reloadHandler);`;
<body> <body>
<div id="app">${results.html}</div> <div id="app">${results.html}</div>
<script type="module"> <script type="module">
import {hydrate, createElement as H} from "react"; import {render, createElement as H} from "react";
import * as Twind from "https://esm.sh/v115/@twind/core@1.1.3/es2022/core.mjs"; import * as Twind from "https://esm.sh/v115/@twind/core@1.1.3/es2022/core.mjs";
import {default as App, CSS} from "@eno/app"; import {default as App, CSS} from "@eno/app";
import {Router, Fetch} from "@eno/iso"; import {Router, Fetch} from "@eno/iso";
Twind.install(CSS); Twind.install(CSS);
Fetch.Seed(${JSON.stringify(seed)}); Fetch.Seed(${JSON.stringify(seed)});
hydrate( H(Router.Provider, null, H(App)), document.querySelector("#app")); render( H(Router.Provider, null, H(App)), document.querySelector("#app"));
</script> </script>
</body> </body>
</html>`; </html>`;