Merge pull request 'deep-proxy' (#1) from deep-proxy into master
Reviewed-on: #1
This commit is contained in:
commit
6cd50eed1a
12
.vscode/launch.json
vendored
12
.vscode/launch.json
vendored
@ -1,12 +0,0 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "deno launch",
|
||||
"request": "launch",
|
||||
"type": "node",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"runtimeExecutable": "deno",
|
||||
"runtimeArgs": ["styler.tsx"],
|
||||
}
|
||||
]
|
||||
}
|
97
bundle.d.ts
vendored
Normal file
97
bundle.d.ts
vendored
Normal file
@ -0,0 +1,97 @@
|
||||
export {}
|
||||
declare global
|
||||
{
|
||||
namespace Van {
|
||||
|
||||
interface State<T> {
|
||||
val: T
|
||||
readonly oldVal: T
|
||||
readonly rawVal: T
|
||||
}
|
||||
|
||||
// Defining readonly view of State<T> for covariance.
|
||||
// Basically we want StateView<string> to implement StateView<string | number>
|
||||
type StateView<T> = Readonly<State<T>>
|
||||
|
||||
type Val<T> = State<T> | T
|
||||
|
||||
type Primitive = string | number | boolean | bigint
|
||||
|
||||
// deno-lint-ignore no-explicit-any
|
||||
type PropValue = Primitive | ((e: any) => void) | null
|
||||
|
||||
type PropValueOrDerived = PropValue | StateView<PropValue> | (() => PropValue)
|
||||
|
||||
type Props = Record<string, PropValueOrDerived> & { class?: PropValueOrDerived; is?: string }
|
||||
|
||||
type PropsWithKnownKeys<ElementType> = Partial<{[K in keyof ElementType]: PropValueOrDerived}>
|
||||
|
||||
type ValidChildDomValue = Primitive | Node | null | undefined
|
||||
|
||||
type BindingFunc = ((dom?: Node) => ValidChildDomValue) | ((dom?: Element) => Element)
|
||||
|
||||
type ChildDom = ValidChildDomValue | StateView<Primitive | null | undefined> | BindingFunc | readonly ChildDom[]
|
||||
|
||||
type TagFunc<Result> = (first?: Props & PropsWithKnownKeys<Result> | ChildDom, ...rest: readonly ChildDom[]) => Result
|
||||
|
||||
type Tags = Readonly<Record<string, TagFunc<Element>>> & {
|
||||
[K in keyof HTMLElementTagNameMap]: TagFunc<HTMLElementTagNameMap[K]>
|
||||
}
|
||||
}
|
||||
const van:{
|
||||
readonly state: <T>(initVal: T, HMRKey?:string)=> Van.State<T>
|
||||
readonly derive: <T>(f: () => T) => Van.State<T>
|
||||
readonly add: (dom: Element, ...children: readonly Van.ChildDom[]) => Element
|
||||
readonly tags: Van.Tags & ((namespaceURI: string) => Readonly<Record<string, Van.TagFunc<Element>>>)
|
||||
readonly hydrate: <T extends Node>(dom: T, f: (dom: T) => T | null | undefined) => T
|
||||
};
|
||||
|
||||
|
||||
namespace VanX
|
||||
{
|
||||
type StateOf<T> = { readonly [K in keyof T]: Van.State<T[K]> }
|
||||
type ValueType<T> = T extends (infer V)[] ? V : T[keyof T]
|
||||
type KeyType<T> = T extends unknown[] ? number : string
|
||||
type ReplacementFunc<T> =
|
||||
T extends (infer V)[] ? (items: V[]) => readonly V[] :
|
||||
(items: [string, T[keyof T]][]) => readonly [string, T[keyof T]][]
|
||||
}
|
||||
const vanX:{
|
||||
readonly calc: <R>(f: () => R) => R
|
||||
readonly reactive: <T extends object>(obj: T, HMRKey?:string) => T
|
||||
readonly noreactive: <T extends object>(obj: T) => T
|
||||
readonly stateFields: <T extends object>(obj: T) => VanX.StateOf<T>
|
||||
readonly raw: <T extends object>(obj: T) => T
|
||||
readonly list: <T extends object, ElementType extends Element>(
|
||||
container: (() => ElementType) | ElementType,
|
||||
items: T,itemFunc: (v: Van.State<VanX.ValueType<T>>, deleter: () => void, k: VanX.KeyType<T>) => Node
|
||||
) => ElementType
|
||||
readonly replace: <T extends object>(obj: T, replacement: VanX.ReplacementFunc<T> | T) => T
|
||||
readonly compact: <T extends object>(obj: T) => T
|
||||
// my addition
|
||||
readonly Store: <T extends object>(obj:T, key:string)=>T
|
||||
};
|
||||
|
||||
namespace Gale {
|
||||
type KeyQuery = "@";
|
||||
type KeyPseudo = ":";
|
||||
type KeyChild = ".";
|
||||
type KeyGroup = "^";
|
||||
type UserStyles = Partial<CSSStyleDeclaration> & {[key: `${KeyQuery|KeyPseudo|KeyChild|KeyGroup}${string}`]: UserStyles }
|
||||
type UserSheet = Record<string, UserStyles>
|
||||
type CollectKeys<Obj> = {[Key in keyof Obj]: Obj[Key] extends object ? Key | CollectKeys<Obj[Key]> : Key }[keyof Obj]
|
||||
type FilterKeys<Keys> = Keys extends `${KeyChild|KeyGroup}${infer Rest}` ? Keys : never
|
||||
type CrossMultiply<A, B> = A extends string ? B extends string ? `${A}${B}` : never : never
|
||||
type CrossMultiplyRecord<Rec> = keyof Rec | { [K in keyof Rec]: K extends string ? CrossMultiply<K, FilterKeys<CollectKeys<Rec[K]>>> : never }[keyof Rec]
|
||||
type Tier = (selector:string, obj:UserStyles)=>string;
|
||||
type CreateSheet = <T extends UserSheet>(sheet:UserSheet&T)=> ((...args:CrossMultiplyRecord<T>[])=>string)&{CSS:string, DOM:Elemental<CrossMultiplyRecord<T>>}
|
||||
|
||||
|
||||
type Elemental<T extends string> = {[K in keyof HTMLElementTagNameMap]: Circular<T, K>}
|
||||
type Circular<Keys extends string, ElementName extends keyof HTMLElementTagNameMap> = {
|
||||
[K in Keys]: Circular<Keys, ElementName>&Van.TagFunc<HTMLElementTagNameMap[ElementName]>;
|
||||
};
|
||||
|
||||
}
|
||||
const Gale:Gale.CreateSheet
|
||||
}
|
17
bundle.js
Normal file
17
bundle.js
Normal file
@ -0,0 +1,17 @@
|
||||
//van
|
||||
{let e,t,r,o,n,l,s,i,f,h,w,a,d,u,_,c,S,g,y,b,m,v,j,x,O;s=Object.getPrototypeOf,f={},h=s(i={isConnected:1}),w=s(s),a=(e,t,r,o)=>(e??(setTimeout(r,o),new Set)).add(t),d=(e,t,o)=>{let n=r;r=t;try{return e(o)}catch(e){return console.error(e),o}finally{r=n}},u=e=>e.filter(e=>e.t?.isConnected),_=e=>n=a(n,e,()=>{for(let e of n)e.o=u(e.o),e.l=u(e.l);n=l},1e3),c={get val(){return r?.i?.add(this),this.rawVal},get oldVal(){return r?.i?.add(this),this.h},set val(o){r?.u?.add(this),o!==this.rawVal&&(this.rawVal=o,this.o.length+this.l.length?(t?.add(this),e=a(e,this,x)):this.h=o)}},S=e=>({__proto__:c,rawVal:e,h:e,o:[],l:[]}),g=(e,t)=>{let r={i:new Set,u:new Set},n={f:e},l=o;o=[];let s=d(e,r,t);s=(s??document).nodeType?s:new Text(s);for(let e of r.i)r.u.has(e)||(_(e),e.o.push(n));for(let e of o)e.t=s;return o=l,n.t=s},y=(e,t=S(),r)=>{let n={i:new Set,u:new Set},l={f:e,s:t};l.t=r??o?.push(l)??i,t.val=d(e,n,t.rawVal);for(let e of n.i)n.u.has(e)||(_(e),e.l.push(l));return t},b=(e,...t)=>{for(let r of t.flat(1/0)){let t=s(r??0),o=t===c?g(()=>r.val):t===w?g(r):r;o!=l&&e.append(o)}return e},m=(e,t,...r)=>{let[{is:o,...n},...i]=s(r[0]??0)===h?r:[{},...r],a=e?document.createElementNS(e,t,{is:o}):document.createElement(t,{is:o});for(let[e,r]of Object.entries(n)){let o=t=>t?Object.getOwnPropertyDescriptor(t,e)??o(s(t)):l,n=t+","+e,i=f[n]??=o(s(a))?.set??0,h=e.startsWith("on")?(t,r)=>{let o=e.slice(2);a.removeEventListener(o,r),a.addEventListener(o,t)}:i?i.bind(a):a.setAttribute.bind(a,e),d=s(r??0);e.startsWith("on")||d===w&&(r=y(r),d=c),d===c?g(()=>(h(r.val,r.h),a)):h(r)}return b(a,i)},v=e=>({get:(t,r)=>m.bind(l,e,r)}),j=(e,t)=>t?t!==e&&e.replaceWith(t):e.remove(),x=()=>{let r=0,o=[...e].filter(e=>e.rawVal!==e.h);do{t=new Set;for(let e of new Set(o.flatMap(e=>e.l=u(e.l))))y(e.f,e.s,e.t),e.t=l}while(++r<100&&(o=[...t]).length);let n=[...e].filter(e=>e.rawVal!==e.h);e=l;for(let e of new Set(n.flatMap(e=>e.o=u(e.o))))j(e.t,g(e.f,e.t)),e.t=l;for(let e of n)e.h=e.rawVal},O={tags:new Proxy(e=>new Proxy(m,v(e)),v()),hydrate:(e,t)=>j(e,g(t,e)),add:b,state:S,derive:y},window.van=O;}
|
||||
//vanx
|
||||
{let e,t,r,{fromEntries:o,entries:l,keys:n,hasOwn:f,getPrototypeOf:a}=Object,{get:i,set:y,deleteProperty:c,ownKeys:s}=Reflect,{state:m,derive:d,add:u}=van,b=1e3,w=Symbol(),A=Symbol(),S=Symbol(),_=Symbol(),g=Symbol(),p=Symbol(),P=e=>(e[A]=1,e),v=e=>e instanceof Object&&!(e instanceof Function)&&!e[p],h=e=>{if(e?.[A]){let t=m();return d(()=>{let r=e();v(t.rawVal)&&v(r)?B(t.rawVal,r):t.val=x(r)}),t}return m(x(e))},F=e=>{let t=Array.isArray(e)?[]:{__proto__:a(e)};for(let[r,o]of l(e))t[r]=h(o);return t[S]=[],t[_]=m(1),t},O={get:(e,t,r)=>t===w?e:f(e,t)?Array.isArray(e)&&"length"===t?(e[_].val,e.length):e[t].val:i(e,t,r),set:(e,o,l,n)=>f(e,o)?Array.isArray(e)&&"length"===o?(l!==e.length&&++e[_].val,e.length=l,1):(e[o].val=x(l),1):o in e?y(e,o,l,n):y(e,o,h(l))&&(++e[_].val,C(e).forEach(E.bind(t,n,o,e[o],r)),1),deleteProperty:(e,t)=>(c(e,t)&&R(e,t),++e[_].val),ownKeys:e=>(e[_].val,s(e))},x=e=>!v(e)||e[w]?e:new Proxy(F(e),O),D=e=>(e[p]=1,e),j=e=>e[w],K=a(m()),N=e=>new Proxy(e,{get:(e,t,r)=>a(e[t]??0)===K?{val:k(e[t].rawVal)}:i(e,t,r)}),k=e=>e?.[w]?new Proxy(N(e[w]),O):e,C=e=>e[S]=e[S].filter(e=>e.t.isConnected),E=(e,t,r,o,{t:l,f:f})=>{let a=Array.isArray(e),i=a?Number(t):t;u(l,()=>l[g][t]=f(r,()=>delete e[t],i)),a&&!o&&i!==e.length-1&&l.insertBefore(l.lastChild,l[g][n(e).find(e=>Number(e)>i)])},R=(e,t)=>{for(let r of C(e)){let e=r.t[g];e[t]?.remove(),delete e[t]}},T=r=>(e??(setTimeout(()=>(e.forEach(C),e=t),b),e=new Set)).add(r),q=(e,t,r)=>{let o={t:e instanceof Function?e():e,f:r},n=t[w];o.t[g]={},n[S].push(o),T(n);for(let[e,r]of l(n))E(t,e,r,1,o);return o.t},z=(e,t)=>{for(let[r,o]of l(t)){let t=e[r];v(t)&&v(o)?z(t,o):e[r]=o}for(let r in e)f(t,r)||delete e[r];let r=n(t),o=Array.isArray(e);if(o||n(e).some((e,t)=>e!==r[t])){let l=e[w];if(o)e.length=t.length;else{++l[_].val;let e={...l};for(let e of r)delete l[e];for(let t of r)l[t]=e[t]}for(let{t:e}of C(l)){let{firstChild:t,[g]:o}=e;for(let l of r)t===o[l]?t=t.nextSibling:e.insertBefore(o[l],t)}}return e},B=(e,n)=>{r=1;try{return z(e,n instanceof Function?Array.isArray(e)?n(e.filter(e=>1)):o(n(l(e))):n)}finally{r=t}},G=e=>Array.isArray(e)?e.filter(e=>1).map(G):v(e)?o(l(e).map(([e,t])=>[e,G(t)])):e;window.vanX={calc:P,reactive:x,noreactive:D,stateFields:j,raw:k,list:q,replace:B,compact:G}}
|
||||
|
||||
//Store
|
||||
vanX.Store =(obj, key)=> {
|
||||
const recall = localStorage.getItem(key);
|
||||
const store = vanX.reactive(recall ? JSON.parse(recall) : obj);
|
||||
van.derive(() => localStorage.setItem(key, JSON.stringify(vanX.compact(store))));
|
||||
return store;
|
||||
}
|
||||
|
||||
//gale
|
||||
const KeyQuery="@",KeyPseudo=":",KeyChild=".",KeyGroup="^",Tier=(e,t)=>`${e}{${Object.keys(t).map((e=>{const n=t[e];switch(e[0]){case"@":return Tier(`@media(max-width:${e.substring(1)})`,n);case":":return Tier(`&${e}`,n);case".":return Tier(`${e}`,n);case"^":return Tier(`&:hover .${e.substring(1)}`,n)}return`${e.replace(/([a-z])([A-Z])/g,"$1-$2")}: ${n};`})).join("\n")}}`;let i=0;globalThis.Gale=e=>{const t=i?"_"+i:"";i++;const n=Object.keys(e).map((t=>Tier("."+t,e[t]))).join("\n");globalThis.document?.head.insertAdjacentHTML("beforeend",`<style data-sheet="${i}">${n}</style>`);const r=(...e)=>{const n=(e,t)=>{const n=t.lastIndexOf(e)+e.length;return n?t.substring(n):t};return e.map((e=>n("^",n(".",e)))).join(t+" ")+t};return r.CSS=n,r.DOM=new Proxy({},{get(e,t){const n=t,r=[],s=new Proxy(((...e)=>{const t=van.tags[n](...e);return t.className=r.join(" "),t}),{get:(e,t)=>(r.push(t.substring(t.lastIndexOf(".")+1)),s)});return s}}),r};
|
||||
//boot
|
||||
((t="/")=>{fetch(t+"deno.json").then((t=>t.json())).then((e=>{const n=(t,e)=>{let n=document.createElement("script");n.type=t,n.textContent=e,document.head.appendChild(n)},o=e.imports;for(let e in o){const n=o[e];n.startsWith("./")&&(o[e]=t+n.substring(2))}n("importmap",JSON.stringify({imports:o})),n("module",'import "entry"; ')}))})();
|
16
deno.json
16
deno.json
@ -1,11 +1,13 @@
|
||||
{
|
||||
"imports": {
|
||||
"react": "https://esm.sh/preact@10.18.1/compat",
|
||||
"react/": "https://esm.sh/preact@10.18.1/compat/"
|
||||
},
|
||||
"tasks": {"go": "deno run -A styler.tsx"},
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
"lib": ["deno.window","dom","dom.asynciterable"]
|
||||
"checkJs": true,
|
||||
"lib": [
|
||||
"deno.window",
|
||||
"DOM"
|
||||
],
|
||||
"types": ["./bundle.d.ts"]
|
||||
},
|
||||
"imports": {
|
||||
"entry":"./app.js"
|
||||
}
|
||||
}
|
94
dev_server.ts
Normal file
94
dev_server.ts
Normal file
@ -0,0 +1,94 @@
|
||||
import { contentType } from "jsr:@std/media-types";
|
||||
|
||||
// Parse the port from the command-line arguments, defaulting to 8000
|
||||
const port = parseInt(Deno.args[0] || "8000", 10);
|
||||
const sockets: WebSocket[] = [];
|
||||
const connect = `<script>
|
||||
const ws = new WebSocket('ws://localhost:${port}/ws');
|
||||
ws.addEventListener('message', (event) => {
|
||||
if (event.data === 'reload') {
|
||||
window.location.reload();
|
||||
}
|
||||
});
|
||||
ws.addEventListener('error', console.error);
|
||||
ws.addEventListener('close', console.warn);
|
||||
</script>`;
|
||||
|
||||
const bundle = await fetch(import.meta.resolve("./bundle.js")).then(r=>r.text());
|
||||
|
||||
let html: string;
|
||||
try {
|
||||
html = Deno.readTextFileSync("./index.html").replace("<head>", "<head>"+connect);
|
||||
} catch (_) {
|
||||
html = `
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
${connect}
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style> * {margin: 0;padding: 0;box-sizing: border-box;}html, body {height: 100%;width: 100%;font-family: Arial, sans-serif;line-height: 1.6;}body {-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;}img, video {max-width: 100%;height: auto;}a {text-decoration: none;color: inherit;}ul, ol {list-style: none;}button, input, textarea {font-family: inherit;font-size: inherit;line-height: inherit;border: none;background: none;padding: 0;margin: 0;outline: none;}table {border-collapse: collapse;width: 100%;}</style>
|
||||
<script>${bundle}</script>
|
||||
</head>
|
||||
<body></body>
|
||||
</html>`;
|
||||
}
|
||||
|
||||
|
||||
function extension(path: string): string {
|
||||
// Remove trailing slash if it exists
|
||||
const normalizedPath = path.endsWith("/") ? path.slice(0, -1) : path;
|
||||
// Get the last part of the path
|
||||
const lastPart = normalizedPath.split("/").pop() || "";
|
||||
// Check if the last part contains a "."
|
||||
return lastPart.split(".")[1] || "";
|
||||
}
|
||||
|
||||
// Start the HTTP server using Deno.serve
|
||||
Deno.serve({ port }, async (req: Request) => {
|
||||
const url = new URL(req.url);
|
||||
|
||||
// Handle WebSocket connections
|
||||
if (url.pathname === "/ws") {
|
||||
const { socket, response } = Deno.upgradeWebSocket(req);
|
||||
sockets.push(socket);
|
||||
return response;
|
||||
}
|
||||
|
||||
// Serve static files or the predefined HTML for non-file routes
|
||||
const path = new URL(req.url).pathname;
|
||||
const ext = extension(path);
|
||||
|
||||
// Serve the predefined HTML for non-file routes
|
||||
if (!ext) {
|
||||
return new Response(html, {
|
||||
headers: { "Content-Type": "text/html" },
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const file = await Deno.open("." + path, { read: true });
|
||||
return new Response(file.readable, {
|
||||
headers: { "Content-Type": contentType(ext) || "application/javascript" },
|
||||
});
|
||||
} catch (err) {
|
||||
if (err instanceof Deno.errors.NotFound) {
|
||||
return new Response("File not found", { status: 404 });
|
||||
} else {
|
||||
return new Response("Internal server error", { status: 500 });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Start watching for file changes
|
||||
const watcher = Deno.watchFs(".");
|
||||
for await (const event of watcher) {
|
||||
if (event.kind === "modify") {
|
||||
for (const ws of sockets) {
|
||||
if (ws.readyState === WebSocket.OPEN) {
|
||||
ws.send("reload");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
import {config, Complex} from "./gale.tsx";
|
||||
|
||||
export default config({other:"lol"});
|
92
gale.tsx
92
gale.tsx
@ -1,92 +0,0 @@
|
||||
|
||||
const typeface = {
|
||||
sans: "sans serif",
|
||||
serif: "Times New Roman"
|
||||
};
|
||||
const sizes = {
|
||||
small: "1rem",
|
||||
large: "3rem"
|
||||
};
|
||||
const colors = {
|
||||
red: "#ff2200",
|
||||
blue: "#0022ff"
|
||||
};
|
||||
const responsive = {
|
||||
md: "min-width:767px",
|
||||
lg: "min-width:1024px",
|
||||
};
|
||||
|
||||
|
||||
const styles = new Map<string, string>();
|
||||
function Mapper<T extends Record<string, string>>(className:string, propertyName:string, lut:T)
|
||||
{
|
||||
const build =(propertyValue:string, customPropertyName?:string):string=>
|
||||
{
|
||||
const selector = `${className}-${(customPropertyName||propertyValue as string).replace(/[^a-zA-Z0-9]/g, "")}${activeQuery.val?"_"+activeQuery.key:""}`;
|
||||
const check = styles.get(selector);
|
||||
if(!check)
|
||||
{
|
||||
const body = `{ ${propertyName}:${propertyValue}; }`;
|
||||
styles.set(selector, activeQuery.val ? `{ @media(${activeQuery.val})${body}}` : body);
|
||||
}
|
||||
return selector;
|
||||
}
|
||||
type UseCustom = (custom:string)=>string;
|
||||
return new Proxy(((override:string)=>build(override)) as UseCustom,
|
||||
{
|
||||
get(_, prop){
|
||||
|
||||
return build(lut[prop as string], prop as string);
|
||||
}
|
||||
}
|
||||
) as T & UseCustom;
|
||||
}
|
||||
function MapperQuery<T extends Record<string, string>>(lut:T)
|
||||
{
|
||||
type UseCustom = (custom:string)=>QueryInvoker;
|
||||
return new Proxy(((override:string)=>Query(override, override)) as UseCustom,
|
||||
{
|
||||
get(_, prop:string){
|
||||
return Query(lut[prop], prop)
|
||||
}
|
||||
}
|
||||
) as Record<keyof T, QueryInvoker> & UseCustom;
|
||||
}
|
||||
|
||||
|
||||
let activeQuery:ActiveQuery;
|
||||
type ActiveQuery = {key:string|false, val:string|false};
|
||||
const activeQueryStack:ActiveQuery[] = [];
|
||||
type QueryInvoker = (...args:string[])=>string
|
||||
function Query(amount:string, key?:string):QueryInvoker
|
||||
{
|
||||
activeQuery = {key:key||amount, val:amount};
|
||||
activeQueryStack.push(activeQuery);
|
||||
return (...args)=> {
|
||||
|
||||
activeQueryStack.pop()
|
||||
activeQuery = activeQueryStack.at(-1) || {key:false, val:false};
|
||||
return args.join(" ");
|
||||
}
|
||||
}
|
||||
|
||||
export function config<T>(obj:T)
|
||||
{
|
||||
const CSS = {
|
||||
Sheet()
|
||||
{
|
||||
const rules = [];
|
||||
for(const [key, value] of styles.entries())
|
||||
{
|
||||
rules.push(key+value);
|
||||
}
|
||||
return rules.join("\n");
|
||||
},
|
||||
Face: Mapper("Face", "font-family", {...typeface, ...obj}),
|
||||
Pad: Mapper("Pad", "padding", sizes),
|
||||
Q: MapperQuery(responsive)
|
||||
};
|
||||
return CSS;
|
||||
}
|
||||
|
||||
export default config({});
|
@ -1,5 +0,0 @@
|
||||
<style>
|
||||
.py-01{padding:30px 0 30px 0;}
|
||||
.px-01{padding:0 20px 0 20px;}
|
||||
</style>
|
||||
<p class="px-01 py-01">test</p>
|
33
refresh_types.ts
Normal file
33
refresh_types.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import { resolve, toFileUrl } from "https://deno.land/std/path/mod.ts";
|
||||
|
||||
async function main()
|
||||
{
|
||||
// Look for the deno.json file in the current directory
|
||||
const denoJsonPath = resolve("./deno.json");
|
||||
|
||||
// Read and parse the deno.json file
|
||||
const denoJson = JSON.parse(await Deno.readTextFile("./deno.json"));
|
||||
|
||||
// Check if compilerOptions and types are defined
|
||||
if (denoJson.compilerOptions?.types) {
|
||||
const types:string[] = denoJson.compilerOptions.types;
|
||||
|
||||
// Iterate over each type file and cache it
|
||||
for (const typeFile of types) {
|
||||
let lookup:string = typeFile;
|
||||
if(!typeFile.startsWith("http"))
|
||||
{
|
||||
lookup = toFileUrl(resolve(Deno.cwd(), typeFile)).href;
|
||||
}
|
||||
console.log(`Caching ${lookup}`);
|
||||
await import(lookup); // This will cache the file
|
||||
}
|
||||
} else {
|
||||
console.log("No types found in compilerOptions.");
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error("Error:", error);
|
||||
Deno.exit(1);
|
||||
});
|
19
scaffold.ts
Normal file
19
scaffold.ts
Normal file
@ -0,0 +1,19 @@
|
||||
const hostedRoot = import.meta.resolve("../").slice(0, -1);
|
||||
async function Download(file:string, processor = (text:string)=>text)
|
||||
{
|
||||
const pathHosted = hostedRoot + "/scaffold/" + file;
|
||||
try
|
||||
{
|
||||
const fileContents = await fetch(pathHosted).then(resp=>resp.text());
|
||||
await Deno.writeTextFile(file, processor(fileContents));
|
||||
console.log(`Successfully downloaded ${file}`);
|
||||
}
|
||||
catch (error)
|
||||
{
|
||||
console.error(`Error downloading ${file}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
Download("deno.json", t=>t.replaceAll(`{HOSTED}`, hostedRoot));
|
||||
Download("index.html", t=>t.replaceAll(`{HOSTED}`, hostedRoot));
|
||||
Download("app.js");
|
47
scaffold/app.js
Normal file
47
scaffold/app.js
Normal file
@ -0,0 +1,47 @@
|
||||
const {DOM} = Gale({
|
||||
Button: {
|
||||
padding: "20px",
|
||||
background: "orange",
|
||||
".Inner": {
|
||||
fontSize: "10rem"
|
||||
}
|
||||
},
|
||||
Outline: {
|
||||
border: "2px solid orange"
|
||||
},
|
||||
Window:{
|
||||
height: "100vh",
|
||||
border: "2px solid black",
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignItems: "end",
|
||||
justifyContent: "center",
|
||||
gap: "10px"
|
||||
},
|
||||
Ability:{
|
||||
width: "50px",
|
||||
height: "50px",
|
||||
background: "red",
|
||||
transition: "all 0.4s",
|
||||
":hover":{
|
||||
transform:"scale(1.1)"
|
||||
}
|
||||
},
|
||||
Orange:{
|
||||
background:"orange"
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
const UI =()=>
|
||||
{
|
||||
return DOM.div.Window(
|
||||
DOM.div.Ability(),
|
||||
DOM.div.Ability(),
|
||||
DOM.div.Ability(),
|
||||
DOM.div.Ability.Orange(),
|
||||
)
|
||||
}
|
||||
|
||||
van.add(document.body, UI());
|
14
scaffold/deno.json
Normal file
14
scaffold/deno.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"checkJs": true,
|
||||
"lib": [ "deno.window", "DOM"],
|
||||
"types": ["{HOSTED}/bundle.d.ts"]
|
||||
},
|
||||
"tasks": {
|
||||
"dev": "deno run -Ar {HOSTED}/dev_server.ts",
|
||||
"types": "deno run -Ar {HOSTED}/refresh_types.ts"
|
||||
},
|
||||
"imports": {
|
||||
"entry":"./app.js"
|
||||
}
|
||||
}
|
11
scaffold/index.html
Normal file
11
scaffold/index.html
Normal file
@ -0,0 +1,11 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<!-- css reset -->
|
||||
<style> * {margin: 0;padding: 0;box-sizing: border-box;}html, body {height: 100%;width: 100%;font-family: Arial, sans-serif;line-height: 1.6;}body {-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;}img, video {max-width: 100%;height: auto;}a {text-decoration: none;color: inherit;}ul, ol {list-style: none;}button, input, textarea {font-family: inherit;font-size: inherit;line-height: inherit;border: none;background: none;padding: 0;margin: 0;outline: none;}table {border-collapse: collapse;width: 100%;}</style>
|
||||
<!-- bundled source -->
|
||||
<script src="{HOSTED}/bundle.js"></script>
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
15
src/boot.js
Normal file
15
src/boot.js
Normal file
@ -0,0 +1,15 @@
|
||||
(
|
||||
(root="/")=>fetch(root+"deno.json")
|
||||
.then(text=>text.json())
|
||||
.then(json=>{
|
||||
const n=(t,e)=>{let n=document.createElement("script");n.type=t,n.textContent=e,document.head.appendChild(n)};
|
||||
const imports = json.imports;
|
||||
for(let n in imports)
|
||||
{
|
||||
const path=imports[n];
|
||||
path.startsWith("./")&&(imports[n]=root+path.substring(2))
|
||||
}
|
||||
n("importmap",JSON.stringify({imports})),
|
||||
n("module",'import "entry"; ')
|
||||
})
|
||||
)();
|
73
src/gale.js
Normal file
73
src/gale.js
Normal file
@ -0,0 +1,73 @@
|
||||
const KeyQuery = "@";
|
||||
const KeyPseudo = ":";
|
||||
const KeyChild = ".";
|
||||
const KeyGroup = "^";
|
||||
|
||||
/** @type {Gale.Tier} */
|
||||
const Tier=(selector, obj)=>
|
||||
{
|
||||
const styles = Object.keys(obj).map((key)=>
|
||||
{
|
||||
const value = obj[key];
|
||||
switch(key[0])
|
||||
{
|
||||
case KeyQuery :
|
||||
return Tier(`@media(max-width:${key.substring(KeyQuery.length)})`, value);
|
||||
case KeyPseudo :
|
||||
return Tier(`&${key}`, value);
|
||||
case KeyChild :
|
||||
return Tier(`${key}`, value);
|
||||
case KeyGroup :
|
||||
return Tier(`&:hover .${key.substring(KeyGroup.length)}`, value);
|
||||
}
|
||||
return `${ key.replace(/([a-z])([A-Z])/g, '$1-$2') }: ${value};`
|
||||
});
|
||||
return `${selector}{${styles.join("\n")}}`;
|
||||
}
|
||||
|
||||
let i = 0;
|
||||
/** @type {Gale.CreateSheet} */
|
||||
globalThis.Gale =sheet=>
|
||||
{
|
||||
const id = i ? "_"+i : "";
|
||||
i++;
|
||||
const css = Object.keys(sheet).map(key=>Tier("."+key, sheet[key])).join(`\n`);
|
||||
globalThis.document?.head.insertAdjacentHTML("beforeend", `<style data-sheet="${i}">${css}</style>`);
|
||||
const classes =(...args)=>{
|
||||
/** @type {(needle:string, str:string)=>string} */
|
||||
const extractLast =(needle, str)=>{
|
||||
const ind = str.lastIndexOf(needle)+needle.length;
|
||||
return ind ? str.substring(ind) : str;
|
||||
}
|
||||
return args.map((arg)=>extractLast(KeyGroup, extractLast(KeyChild, arg))).join(id+" ")+id;
|
||||
}
|
||||
|
||||
classes.CSS = css;
|
||||
classes.DOM = new Proxy(
|
||||
{},
|
||||
{get(_, prop)
|
||||
{
|
||||
const pending = prop;
|
||||
const mentioned = [];
|
||||
const collector = new Proxy(
|
||||
(...args)=>
|
||||
{
|
||||
const element = van.tags[pending](...args);
|
||||
element.className = mentioned.join(" ");
|
||||
return element;
|
||||
},
|
||||
{
|
||||
get(_, prop)
|
||||
{
|
||||
mentioned.push(prop.substring(prop.lastIndexOf(".")+1));
|
||||
return collector;
|
||||
}
|
||||
}
|
||||
);
|
||||
return collector;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return classes;
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
import Gale from "./gale-custom.tsx";
|
||||
|
||||
const classes = Gale.Q.lg( Gale.Face.sans, Gale.Q.md(Gale.Pad.large), Gale.Pad.small );
|
||||
|
||||
console.log(classes);
|
||||
|
||||
console.log(Gale.Sheet())
|
||||
|
Loading…
Reference in New Issue
Block a user