Compare commits
No commits in common. "8ab67be4d8adc60e0c9db55d803a309ac78b8867" and "d9c5b83f26a42755f0c4b6875e3bb0ea3a99e084" have entirely different histories.
8ab67be4d8
...
d9c5b83f26
24
.vscode/launch.json
vendored
24
.vscode/launch.json
vendored
@ -1,24 +0,0 @@
|
|||||||
{
|
|
||||||
// Use IntelliSense to learn about possible attributes.
|
|
||||||
// Hover to view descriptions of existing attributes.
|
|
||||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"request": "launch",
|
|
||||||
"name": "Launch Program",
|
|
||||||
"type": "node",
|
|
||||||
"program": "${workspaceFolder}/server.tsx",
|
|
||||||
"cwd": "${workspaceFolder}",
|
|
||||||
"runtimeExecutable": "C:\\Users\\Seth\\.deno\\bin\\deno.EXE",
|
|
||||||
"runtimeArgs": [
|
|
||||||
"run",
|
|
||||||
"--no-lock",
|
|
||||||
"--unstable",
|
|
||||||
"--inspect-wait",
|
|
||||||
"--allow-all"
|
|
||||||
],
|
|
||||||
"attachSimplePort": 9229
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
16
deno.lock
16
deno.lock
@ -1,16 +0,0 @@
|
|||||||
{
|
|
||||||
"version": "2",
|
|
||||||
"remote": {
|
|
||||||
"http://localhost:4507/lib/iso.tsx": "edf1cf4c539900040c75824eac8472e0ec43098b13af4049bd79ddbd76a5346d",
|
|
||||||
"https://esm.sh/preact@10.13.2/compat": "1cf68e0c8c6c84b60d42f30665403b67229c16ff5206824709b19df60ba9cdc3",
|
|
||||||
"https://esm.sh/stable/preact@10.13.2/deno/compat.js": "3151a948abd84aa75dfc9e57733da7e1a45fff7a25de58c7b6025b923874b508",
|
|
||||||
"https://esm.sh/stable/preact@10.13.2/deno/hooks.js": "c7a8e703bcbc6a05949f329b618c33d5d1ea5fee113ddcea44ff0f527af8556f",
|
|
||||||
"https://esm.sh/stable/preact@10.13.2/deno/preact.mjs": "365fab897381f4f403f859c5d12939084560545567108cc90dae901bbe892578",
|
|
||||||
"https://esm.sh/v116/preact@10.13.2/compat/src/index.d.ts": "d02f015638a40e32649151e011cfda7b520d66f7fbd3c12a28fa03de2a5e1421",
|
|
||||||
"https://esm.sh/v116/preact@10.13.2/compat/src/suspense-list.d.ts": "b8e274324392157ce476ef3a48ae215c9f7003b08525d140645f19eab20d1948",
|
|
||||||
"https://esm.sh/v116/preact@10.13.2/compat/src/suspense.d.ts": "81f5266e0977a94347505d11b8103024211f2b4f3b2eb2aa276a10d8fd169e65",
|
|
||||||
"https://esm.sh/v116/preact@10.13.2/hooks/src/index.d.ts": "5c29febb624fc25d71cb0e125848c9b711e233337a08f7eacfade38fd4c14cc3",
|
|
||||||
"https://esm.sh/v116/preact@10.13.2/src/index.d.ts": "65398710de6aa0a07412da79784e05e6e96763f51c7c91b77344d2d0af06385c",
|
|
||||||
"https://esm.sh/v116/preact@10.13.2/src/jsx.d.ts": "9ac9b82c199fa7b04748807d750eba1a106c0be52041b8617416f88d6fc0a257"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,24 +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!"/>
|
||||||
<nav class="p-4">
|
<h1 class="my-2 font(bold serif) text(2xl)">Title!!</h1>
|
||||||
<a class="text-red-500" href="/">Home</a>
|
<h2>subtitle</h2>
|
||||||
<a href="/about">About</a>
|
<Component/>
|
||||||
</nav>
|
|
||||||
|
|
||||||
<h1 class="my-2 font(bold serif) text(3xl)">Title!!</h1>
|
|
||||||
<h2>suspended:</h2>
|
|
||||||
<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>
|
||||||
|
@ -4,20 +4,14 @@ import * as Iso from "@eno/iso";
|
|||||||
export default ()=>
|
export default ()=>
|
||||||
{
|
{
|
||||||
const [countGet, countSet] = React.useState(1);
|
const [countGet, countSet] = React.useState(1);
|
||||||
|
|
||||||
const [routeGet, routeSet] = Iso.Router.Consumer();
|
const [routeGet, routeSet] = Iso.Router.Consumer();
|
||||||
|
|
||||||
type CatFact = {fact:string, length:number}|undefined;
|
|
||||||
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-green-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>Status:{Updating?'loading':'done'}</p>
|
|
||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
@ -1,35 +0,0 @@
|
|||||||
import {Fetch} from "./lib/iso.tsx";
|
|
||||||
|
|
||||||
|
|
||||||
const delay =async(inHandler:()=>void, inDelay:number):Promise<void>=>
|
|
||||||
{
|
|
||||||
return new Promise((accept)=>{
|
|
||||||
setTimeout(()=>{
|
|
||||||
accept(inHandler());
|
|
||||||
}, inDelay);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const queue = [1, 2, 3];
|
|
||||||
while(queue.length)
|
|
||||||
{
|
|
||||||
await(delay(()=>{console.log(queue.pop())}, 1000))
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("all done!");
|
|
||||||
|
|
||||||
/*
|
|
||||||
let r1, r2, r3;
|
|
||||||
|
|
||||||
delay(()=>{r1 = Fetch.Request(`https://catfact.ninja/fact`, undefined, 0.2); console.log(r1); }, 10);
|
|
||||||
delay(()=>{r2 = Fetch.Request(`https://catfact.ninja/fact`, undefined, 0.2); console.log(r2); }, 20);
|
|
||||||
delay(()=>{r3 = Fetch.Request(`https://catfact.ninja/fact`, undefined, 0.2); console.log(r3); }, 2000);
|
|
||||||
|
|
||||||
|
|
||||||
await delay(()=>{}, 3000);
|
|
||||||
|
|
||||||
console.log(r1);
|
|
||||||
console.log(r2);
|
|
||||||
console.log(r3);
|
|
||||||
|
|
||||||
*/
|
|
111
lib/iso.tsx
111
lib/iso.tsx
@ -158,113 +158,4 @@ export const Switch =({children}:{children:Children})=>
|
|||||||
return fallback;
|
return fallback;
|
||||||
};
|
};
|
||||||
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 FetchCachOptions = {CacheFor:number, CacheOnServer:boolean, DelaySSR:boolean, Seed:boolean};
|
|
||||||
export type FetchRecord = {URL:string, Promise?:Promise<FetchRecord>, CachedAt:number, Error?:string, JSON?:object} & FetchCachOptions;
|
|
||||||
type FetchGuide = [Record:FetchRecord, Init:boolean, Listen:boolean];
|
|
||||||
export type FetchHookState = [Data:undefined|object, Updating:boolean];
|
|
||||||
export const Fetch = {
|
|
||||||
Cache:new Map() as Map<string, FetchRecord>,
|
|
||||||
ServerBlocking:false as false|Promise<FetchRecord>[],
|
|
||||||
ServerTouched:false as false|Set<FetchRecord>,
|
|
||||||
ServerRemove:false as false|Set<FetchRecord>,
|
|
||||||
Seed(seed:FetchRecord[])
|
|
||||||
{
|
|
||||||
seed.forEach(r=>{
|
|
||||||
//r.Promise = Promise.resolve(r);
|
|
||||||
Fetch.Cache.set(r.URL, r)
|
|
||||||
});
|
|
||||||
},
|
|
||||||
DefaultOptions:{CacheFor:60, CacheOnServer:true, DelaySSR:true, Seed:true} as FetchCachOptions,
|
|
||||||
Request(URL:string, Init?:RequestInit|null, CacheFor:number = 60, CacheOnServer:boolean = true, DelaySSR:boolean = true, Seed:boolean = true):FetchGuide
|
|
||||||
{
|
|
||||||
let check = Fetch.Cache.get(URL);
|
|
||||||
|
|
||||||
const load =(inCheck:FetchRecord)=>
|
|
||||||
{
|
|
||||||
Fetch.Cache.set(URL, inCheck);
|
|
||||||
inCheck.CachedAt = 0;
|
|
||||||
inCheck.Promise = fetch(URL, Init?Init:undefined).then(resp=>resp.json()).then((json)=>{
|
|
||||||
inCheck.JSON = json;
|
|
||||||
inCheck.CachedAt = new Date().getTime();
|
|
||||||
console.log(`...cached!`);
|
|
||||||
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}), 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, check.JSON ? true : false, true];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
console.log(`found in cache...`);
|
|
||||||
let secondsAge = (new Date().getTime() - check.CachedAt)/1000;
|
|
||||||
if(secondsAge > check.CacheFor)
|
|
||||||
{
|
|
||||||
// cached but expired
|
|
||||||
// - listen
|
|
||||||
// - init
|
|
||||||
console.log(`...outdated...`);
|
|
||||||
return [load(check), true, true];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// cached and ready
|
|
||||||
// - init
|
|
||||||
console.log(`...retrieved!`);
|
|
||||||
return [check, true, false];
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
Use(URL:string, Init?:RequestInit|null, Options?:FetchCachOptions)
|
|
||||||
{
|
|
||||||
const config = {...Fetch.DefaultOptions, ...Options};
|
|
||||||
const [receipt, init, listen] = Fetch.Request(URL, Init, config.CacheFor, config.CacheOnServer, config.DelaySSR, config.Seed);
|
|
||||||
const initialState:FetchHookState = init ? [receipt.JSON, listen] : [undefined, true];
|
|
||||||
const [cacheGet, cacheSet] = React.useState(initialState);
|
|
||||||
|
|
||||||
if(Fetch.ServerBlocking && Fetch.ServerTouched && config.DelaySSR) // if server-side rendering
|
|
||||||
{
|
|
||||||
if(listen) // if the request is pending
|
|
||||||
{
|
|
||||||
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 request is ready
|
|
||||||
{
|
|
||||||
receipt.Seed && Fetch.ServerTouched.add(receipt); // add record to client seed list (if specified in receipt.seed)
|
|
||||||
return [receipt.JSON, false] as FetchHookState;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
React.useEffect(()=>
|
|
||||||
{
|
|
||||||
if(listen)
|
|
||||||
{
|
|
||||||
//const receipt = Fetch.Request(URL, Init, CacheFor, CacheOnServer, DelaySSR);
|
|
||||||
receipt.Promise?.then(()=>cacheSet([receipt.JSON, receipt.CachedAt === 0]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
, []);
|
|
||||||
|
|
||||||
return cacheGet;
|
|
||||||
}
|
|
||||||
};
|
|
42
server.tsx
42
server.tsx
@ -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;
|
||||||
};
|
};
|
||||||
@ -223,36 +221,10 @@ FileListen("${url.pathname}", reloadHandler);`;
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Iso.Fetch.ServerBlocking = [];
|
const results = Twind.extract(SSR(<Iso.Router.Provider url={url}><App/></Iso.Router.Provider>), TwindInst);
|
||||||
Iso.Fetch.ServerTouched = new Set();
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
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.Router.Provider url={url}><App/></Iso.Router.Provider>;
|
|
||||||
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`;
|
type = `text/html`;
|
||||||
body =
|
body = `<!doctype html>
|
||||||
`<!doctype html>
|
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<title>${Iso.Meta.title}</title>
|
<title>${Iso.Meta.title}</title>
|
||||||
@ -264,14 +236,12 @@ 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} from "@eno/iso";
|
||||||
Twind.install(CSS);
|
Twind.install(CSS);
|
||||||
Fetch.Seed(${JSON.stringify(seed)});
|
render( H(Router.Provider, null, H(App)), document.querySelector("#app"));
|
||||||
const hmrWrap = H( ()=>H(App) );
|
|
||||||
hydrate( H(Router.Provider, null, hmrWrap), document.querySelector("#app"));
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>`;
|
</html>`;
|
||||||
|
Loading…
Reference in New Issue
Block a user