issue/fetch #21
@ -1,16 +1,19 @@
 | 
				
			|||||||
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>subtitle</h2>
 | 
					        <h2>suspended:</h2>
 | 
				
			||||||
        <Component/>
 | 
					        <React.Suspense fallback={<div>Loading!</div>}>
 | 
				
			||||||
 | 
					            <Comp/>
 | 
				
			||||||
 | 
					        </React.Suspense>
 | 
				
			||||||
        <Iso.Switch>
 | 
					        <Iso.Switch>
 | 
				
			||||||
            <Iso.Case value="page">
 | 
					            <Iso.Case value="page">
 | 
				
			||||||
                <Iso.Switch>
 | 
					                <Iso.Switch>
 | 
				
			||||||
 | 
				
			|||||||
@ -7,13 +7,15 @@ 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`);
 | 
					    const [Data, Updating] = Iso.Fetch.Use(`https://catfact.ninja/fact`, undefined, 60, true, true, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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!"/>
 | 
				
			||||||
        Route is: {routeGet.Path.toString()}
 | 
					        Component 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>
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										56
									
								
								lib/iso.tsx
									
									
									
									
									
								
							
							
						
						
									
										56
									
								
								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 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, 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 = {
 | 
					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>[],
 | 
				
			||||||
@ -169,11 +171,11 @@ export const Fetch = {
 | 
				
			|||||||
    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)
 | 
					    Request(URL:string, Init?:RequestInit, CacheFor:number = 60, CacheOnServer:boolean = true, DelaySSR:boolean = true, Seed:boolean = true):FetchGuide
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        let check = Fetch.Cache.get(URL);
 | 
					        let check = Fetch.Cache.get(URL);
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
@ -188,17 +190,24 @@ 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});
 | 
					            return [load({URL, CacheFor, CachedAt:0, CacheOnServer, DelaySSR, Seed}), false, true];
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        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;
 | 
					            return [check, check.JSON ? true : false, true];
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        else
 | 
					        else
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@ -206,38 +215,49 @@ 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);
 | 
					                return [load(check), true, true];
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 | 
					                // cached and ready
 | 
				
			||||||
 | 
					                // - init
 | 
				
			||||||
                console.log(`...retrieved!`);
 | 
					                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)
 | 
					    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 [receipt, init, listen] = Fetch.Request(URL, Init, CacheFor, CacheOnServer, DelaySSR, Seed);
 | 
				
			||||||
        const [cacheGet, cacheSet] = React.useState([undefined, true] as FetchHookState);
 | 
					        const initialState:FetchHookState = init ? [receipt.JSON, listen] : [undefined, true];
 | 
				
			||||||
        if(Fetch.ServerBlocking && Fetch.ServerTouched && DelaySSR)
 | 
					        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(listen) // if the request is pending
 | 
				
			||||||
            if(receipt.CachedAt === 0 && receipt.Promise)// request for something that hasnt been cached yet
 | 
					 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                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(()=>
 | 
					        React.useEffect(()=>
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            const receipt = Fetch.Request(URL, Init, CacheFor, CacheOnServer, DelaySSR);
 | 
					            if(listen)
 | 
				
			||||||
            receipt.Promise?.then(()=>cacheSet([receipt.JSON, receipt.CachedAt === 0]));
 | 
					            {
 | 
				
			||||||
 | 
					                //const receipt = Fetch.Request(URL, Init, CacheFor, CacheOnServer, DelaySSR);
 | 
				
			||||||
 | 
					                receipt.Promise?.then(()=>cacheSet([receipt.JSON, receipt.CachedAt === 0]));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        , []);
 | 
					        , []);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										16
									
								
								server.tsx
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								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 * 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";
 | 
				
			||||||
@ -35,7 +36,8 @@ 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", minify:true});
 | 
					    const transpile = await ESBuild.transform(inCode, { loader: "tsx", sourcemap:"inline", minify:true, sourcefile:inKey});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Transpiled.set(inKey, transpile.code);
 | 
					    Transpiled.set(inKey, transpile.code);
 | 
				
			||||||
    return transpile.code;
 | 
					    return transpile.code;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
@ -224,14 +226,18 @@ FileListen("${url.pathname}", reloadHandler);`;
 | 
				
			|||||||
            Iso.Fetch.ServerBlocking = [];
 | 
					            Iso.Fetch.ServerBlocking = [];
 | 
				
			||||||
            Iso.Fetch.ServerTouched = new Set();
 | 
					            Iso.Fetch.ServerTouched = new Set();
 | 
				
			||||||
            Iso.Fetch.ServerRemove = new Set();
 | 
					            Iso.Fetch.ServerRemove = new Set();
 | 
				
			||||||
            let bake = SSR(<Iso.Router.Provider url={url}><App/></Iso.Router.Provider>);
 | 
					            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
 | 
					                // 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.
 | 
					                // this next render will use cached resources. using a cached resource (if its "Seed" is true) adds it to the "touched" set.
 | 
				
			||||||
                bake = SSR(<Iso.Router.Provider url={url}><App/></Iso.Router.Provider>);
 | 
					                app = <Iso.Router.Provider url={url}><App/></Iso.Router.Provider>;
 | 
				
			||||||
 | 
					                await Prepass(app)
 | 
				
			||||||
 | 
					                bake = SSR(app);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const seed:Iso.FetchRecord[] = [];
 | 
					            const seed:Iso.FetchRecord[] = [];
 | 
				
			||||||
@ -258,13 +264,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 {render, createElement as H} from "react";
 | 
					            import {hydrate, 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)});
 | 
				
			||||||
            render( H(Router.Provider, null, H(App)), document.querySelector("#app"));
 | 
					            hydrate( H(Router.Provider, null, H(App)), document.querySelector("#app"));
 | 
				
			||||||
        </script>
 | 
					        </script>
 | 
				
			||||||
    </body>
 | 
					    </body>
 | 
				
			||||||
</html>`;
 | 
					</html>`;
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user