ajo 0.1.22 → 0.1.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=Symbol.for("ajo.context"),r=(t,e=Symbol())=>function(...l){const n=this??c;return n?l.length?n[o][e]=l[0]:e in n[o]?n[o][e]:t:t};let c=null;const u=(...t)=>t.length?c=t[0]:c;exports.Context=o;exports.context=r;exports.current=u;
@@ -0,0 +1,11 @@
1
+ const e = Symbol.for("ajo.context"), r = (t, o = Symbol()) => function(...l) {
2
+ const n = this ?? c;
3
+ return n ? l.length ? n[e][o] = l[0] : o in n[e] ? n[e][o] : t : t;
4
+ };
5
+ let c = null;
6
+ const s = (...t) => t.length ? c = t[0] : c;
7
+ export {
8
+ e as Context,
9
+ r as context,
10
+ s as current
11
+ };
package/dist/html.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const g=Symbol.for("ajo.args"),c=Symbol.for("ajo.context"),f="",$=new Set(["area","base","br","col","command","embed","hr","img","input","keygen","link","meta","param","source","track","wbr"]),p=new Set(["key","skip","memo","ref"]),S=new Set(["nodeName","children"].concat(Array.from(p))),m=e=>Array.from(y(e)).join(""),y=function*(e){for(e of N(e))if(typeof e=="string")yield u(e);else{let t="";for(const[n,o]of Object.entries(e))S.has(n)||n.startsWith("set:")||o==null||o===!1||(t+=o===!0?` ${n}`:` ${n}="${u(String(o))}"`);$.has(e.nodeName)?yield`<${e.nodeName}${t}>`:e.skip?yield`<${e.nodeName}${t}></${e.nodeName}>`:(yield`<${e.nodeName}${t}>`,typeof e.children=="string"&&e.children.startsWith(f)?yield e.children.slice(1):yield*y(e.children),yield`</${e.nodeName}>`)}},N=function*(e,t={h:""},n=!0){for(e of Array.isArray(e)?e.flat(1/0):[e]){const o=typeof e;if(!(e==null||o==="boolean"))if(o==="object"&&"nodeName"in e)if(t.h&&(yield t.h,t.h=""),typeof e.nodeName=="function")if(e.nodeName.constructor.name==="GeneratorFunction"){const r=Object.assign({},e.nodeName.attrs),l=Object.assign({},e.nodeName.args);for(const a in e){const d=e[a];a.startsWith("attr:")?r[a.slice(5)]=d:p.has(a)||a.startsWith("set:")?r[a]=d:l[a]=d}r.nodeName=e.nodeName.is??"div",r.children=b(e.nodeName,l),yield r}else yield*N(e.nodeName(e),t,!1);else yield e;else t.h+=e}n&&t.h&&(yield t.h)},u=e=>e.replace(/[&<>"']/g,t=>`&#${t.charCodeAt(0)};`),s=()=>{};let i=null;const b=(e,t)=>{let n,o;try{const r=e.call(n={*[Symbol.iterator](){for(;;)yield t},[g]:t,[c]:Object.create((i==null?void 0:i[c])??null),render:s,queueMicrotask:s,requestAnimationFrame:s,effect:s,cleanup:s,next(){if(i===n)return;const l=i;i=n,o=m(r.next().value),i=l},throw(l){o=m(r.throw(l).value)},return(){r.return()}},t);n.next()}catch(r){n.throw(r)}finally{n.return()}return f+o},j=(e,t=Symbol())=>function(...n){const o=this??i;return o?n.length==0?t in o[c]?o[c][t]:e:o[c][t]=n[0]:e};exports.context=j;exports.html=y;exports.render=m;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=require("./context.cjs"),A=new Set(["area","base","br","col","command","embed","hr","img","input","keygen","link","meta","param","source","track","wbr"]),w=new Set(["key","skip","memo","ref"]),$=new Set(["nodeName","children",...w]),j=Symbol.for("ajo.args"),l=e=>e.replace(/[&<>"']/g,r=>`&#${r.charCodeAt(0)};`),m=(e,r)=>({nodeName:"div","data-ssr":e,children:r}),y=()=>{},x=e=>[...v(e)].join(""),v=function*(e,r=()=>"",i=y){yield*g(f(e,r,i))},f=(e,r,i)=>{if(!(e==null||typeof e=="boolean")){if(typeof e=="string"||typeof e=="number")return String(e);if(Array.isArray(e)){const n=[];for(e of e.flat(1/0)){const t=f(e,r,i);t!=null&&n.push(t)}return n.length===1?n[0]:n}if(typeof e=="object"&&"nodeName"in e){if(typeof e.nodeName=="function")return k(e,r,i);const n={nodeName:e.nodeName};for(const t in e)!$.has(t)&&!t.startsWith("set:")&&(n[t]=e[t]);return"children"in e&&(n.children=f(e.children,r,i)),n}return String(e)}},k=({nodeName:e,fallback:r=e.fallback,...i},n,t)=>{if(e.src){const o=n();return t({id:o,src:e.src,h:i,done:!0}),m(o,r)}const s=e.constructor.name;if(s==="GeneratorFunction")return p(e,i,n,t);if(s==="AsyncGeneratorFunction")return G(e,r,i,n,t);if(i=e(i),i instanceof Promise){if(t===y)return r;const o=n();return i.then(a=>t({id:o,h:f(a,(d=o)=>n(d),t),done:!0})),m(o,r)}return f(i,n,t)},p=(e,r,i,n)=>{var S,b;const t={...e.attrs},s={...e.args};for(const c in r)c.startsWith("attr:")?t[c.slice(5)]=r[c]:w.has(c)||c.startsWith("set:")?t[c]=r[c]:s[c]=r[c];const o={[u.Context]:Object.assign(Object.create(null),(S=u.current())==null?void 0:S[u.Context]),[j]:s,render:y,next:y,return:y,throw:c=>{throw c}},a=e.call(o,s),d=u.current();u.current(o);try{return{...t,nodeName:e.is??"div",children:f(a.next().value,i,n)}}finally{(b=a.return)==null||b.call(a),u.current(d)}},G=(e,r,i,n,t)=>{if(t===y)return r;const s=n();return Promise.resolve().then(async()=>{var a;n=(d=s)=>n(d);const o=e(i);try{for(i=await o.next();!i.done;)t({id:s,h:f(i.value,n,t),done:!1}),i=await o.next();t({id:s,h:f(i.value,n,t),done:!0})}catch(d){t({id:s,h:f(d,n,t),done:!0})}finally{(a=o.return)==null||a.call(o)}}),m(s,r)},g=function*(e){if(typeof e=="string")yield l(e);else if(Array.isArray(e))for(e of e)yield*g(e);else if(typeof e=="object"&&"nodeName"in e){const{nodeName:r,children:i}=e;let n="";for(const t in e)$.has(t)||t.startsWith("set:")||e[t]==null||e[t]===!1||(e[t]===!0?n+=` ${t}`:n+=` ${t}="${l(String(e[t]))}"`);A.has(r)?yield`<${r}${n}>`:(yield`<${r}${n}>`,i!=null&&(yield*g(i)),yield`</${r}>`)}else yield l(String(e))};exports.html=v;exports.render=x;
package/dist/html.js CHANGED
@@ -1,72 +1,90 @@
1
- const $ = Symbol.for("ajo.args"), c = Symbol.for("ajo.context"), f = "", g = /* @__PURE__ */ new Set(["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param", "source", "track", "wbr"]), u = /* @__PURE__ */ new Set(["key", "skip", "memo", "ref"]), S = new Set(["nodeName", "children"].concat(Array.from(u))), m = (e) => Array.from(p(e)).join(""), p = function* (e) {
2
- for (e of N(e))
3
- if (typeof e == "string") yield y(e);
4
- else {
5
- let t = "";
6
- for (const [n, o] of Object.entries(e))
7
- S.has(n) || n.startsWith("set:") || o == null || o === !1 || (t += o === !0 ? ` ${n}` : ` ${n}="${y(String(o))}"`);
8
- g.has(e.nodeName) ? yield `<${e.nodeName}${t}>` : e.skip ? yield `<${e.nodeName}${t}></${e.nodeName}>` : (yield `<${e.nodeName}${t}>`, typeof e.children == "string" && e.children.startsWith(f) ? yield e.children.slice(1) : yield* p(e.children), yield `</${e.nodeName}>`);
1
+ import { Context as $, current as u } from "./context.js";
2
+ const v = /* @__PURE__ */ new Set(["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param", "source", "track", "wbr"]), b = /* @__PURE__ */ new Set(["key", "skip", "memo", "ref"]), A = /* @__PURE__ */ new Set(["nodeName", "children", ...b]), j = Symbol.for("ajo.args"), l = (e) => e.replace(/[&<>"']/g, (r) => `&#${r.charCodeAt(0)};`), m = (e, r) => ({ nodeName: "div", "data-ssr": e, children: r }), y = () => {
3
+ }, N = (e) => [...k(e)].join(""), k = function* (e, r = () => "", i = y) {
4
+ yield* g(f(e, r, i));
5
+ }, f = (e, r, i) => {
6
+ if (!(e == null || typeof e == "boolean")) {
7
+ if (typeof e == "string" || typeof e == "number") return String(e);
8
+ if (Array.isArray(e)) {
9
+ const n = [];
10
+ for (e of e.flat(1 / 0)) {
11
+ const t = f(e, r, i);
12
+ t != null && n.push(t);
13
+ }
14
+ return n.length === 1 ? n[0] : n;
15
+ }
16
+ if (typeof e == "object" && "nodeName" in e) {
17
+ if (typeof e.nodeName == "function") return p(e, r, i);
18
+ const n = { nodeName: e.nodeName };
19
+ for (const t in e) !A.has(t) && !t.startsWith("set:") && (n[t] = e[t]);
20
+ return "children" in e && (n.children = f(e.children, r, i)), n;
9
21
  }
10
- }, N = function* (e, t = { h: "" }, n = !0) {
11
- for (e of Array.isArray(e) ? e.flat(1 / 0) : [e]) {
12
- const o = typeof e;
13
- if (!(e == null || o === "boolean"))
14
- if (o === "object" && "nodeName" in e)
15
- if (t.h && (yield t.h, t.h = ""), typeof e.nodeName == "function")
16
- if (e.nodeName.constructor.name === "GeneratorFunction") {
17
- const r = Object.assign({}, e.nodeName.attrs), l = Object.assign({}, e.nodeName.args);
18
- for (const a in e) {
19
- const d = e[a];
20
- a.startsWith("attr:") ? r[a.slice(5)] = d : u.has(a) || a.startsWith("set:") ? r[a] = d : l[a] = d;
21
- }
22
- r.nodeName = e.nodeName.is ?? "div", r.children = j(e.nodeName, l), yield r;
23
- } else yield* N(e.nodeName(e), t, !1);
24
- else yield e;
25
- else t.h += e;
22
+ return String(e);
26
23
  }
27
- n && t.h && (yield t.h);
28
- }, y = (e) => e.replace(/[&<>"']/g, (t) => `&#${t.charCodeAt(0)};`), s = () => {
29
- };
30
- let i = null;
31
- const j = (e, t) => {
32
- let n, o;
24
+ }, p = ({ nodeName: e, fallback: r = e.fallback, ...i }, n, t) => {
25
+ if (e.src) {
26
+ const o = n();
27
+ return t({ id: o, src: e.src, h: i, done: !0 }), m(o, r);
28
+ }
29
+ const s = e.constructor.name;
30
+ if (s === "GeneratorFunction") return x(e, i, n, t);
31
+ if (s === "AsyncGeneratorFunction") return G(e, r, i, n, t);
32
+ if (i = e(i), i instanceof Promise) {
33
+ if (t === y) return r;
34
+ const o = n();
35
+ return i.then((a) => t({ id: o, h: f(a, (d = o) => n(d), t), done: !0 })), m(o, r);
36
+ }
37
+ return f(i, n, t);
38
+ }, x = (e, r, i, n) => {
39
+ var w, S;
40
+ const t = { ...e.attrs }, s = { ...e.args };
41
+ for (const c in r)
42
+ c.startsWith("attr:") ? t[c.slice(5)] = r[c] : b.has(c) || c.startsWith("set:") ? t[c] = r[c] : s[c] = r[c];
43
+ const o = {
44
+ [$]: Object.assign(/* @__PURE__ */ Object.create(null), (w = u()) == null ? void 0 : w[$]),
45
+ [j]: s,
46
+ render: y,
47
+ next: y,
48
+ return: y,
49
+ throw: (c) => {
50
+ throw c;
51
+ }
52
+ }, a = e.call(o, s), d = u();
53
+ u(o);
33
54
  try {
34
- const r = e.call(n = {
35
- *[Symbol.iterator]() {
36
- for (; ; ) yield t;
37
- },
38
- [$]: t,
39
- [c]: Object.create((i == null ? void 0 : i[c]) ?? null),
40
- render: s,
41
- queueMicrotask: s,
42
- requestAnimationFrame: s,
43
- effect: s,
44
- cleanup: s,
45
- next() {
46
- if (i === n) return;
47
- const l = i;
48
- i = n, o = m(r.next().value), i = l;
49
- },
50
- throw(l) {
51
- o = m(r.throw(l).value);
52
- },
53
- return() {
54
- r.return();
55
- }
56
- }, t);
57
- n.next();
58
- } catch (r) {
59
- n.throw(r);
55
+ return { ...t, nodeName: e.is ?? "div", children: f(a.next().value, i, n) };
60
56
  } finally {
61
- n.return();
57
+ (S = a.return) == null || S.call(a), u(d);
62
58
  }
63
- return f + o;
64
- }, w = (e, t = Symbol()) => function(...n) {
65
- const o = this ?? i;
66
- return o ? n.length == 0 ? t in o[c] ? o[c][t] : e : o[c][t] = n[0] : e;
59
+ }, G = (e, r, i, n, t) => {
60
+ if (t === y) return r;
61
+ const s = n();
62
+ return Promise.resolve().then(async () => {
63
+ var a;
64
+ n = (d = s) => n(d);
65
+ const o = e(i);
66
+ try {
67
+ for (i = await o.next(); !i.done; )
68
+ t({ id: s, h: f(i.value, n, t), done: !1 }), i = await o.next();
69
+ t({ id: s, h: f(i.value, n, t), done: !0 });
70
+ } catch (d) {
71
+ t({ id: s, h: f(d, n, t), done: !0 });
72
+ } finally {
73
+ (a = o.return) == null || a.call(o);
74
+ }
75
+ }), m(s, r);
76
+ }, g = function* (e) {
77
+ if (typeof e == "string") yield l(e);
78
+ else if (Array.isArray(e)) for (e of e) yield* g(e);
79
+ else if (typeof e == "object" && "nodeName" in e) {
80
+ const { nodeName: r, children: i } = e;
81
+ let n = "";
82
+ for (const t in e)
83
+ A.has(t) || t.startsWith("set:") || e[t] == null || e[t] === !1 || (e[t] === !0 ? n += ` ${t}` : n += ` ${t}="${l(String(e[t]))}"`);
84
+ v.has(r) ? yield `<${r}${n}>` : (yield `<${r}${n}>`, i != null && (yield* g(i)), yield `</${r}>`);
85
+ } else yield l(String(e));
67
86
  };
68
87
  export {
69
- w as context,
70
- p as html,
71
- m as render
88
+ k as html,
89
+ N as render
72
90
  };
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const b=Symbol.for("ajo.key"),k=Symbol.for("ajo.memo"),p=Symbol.for("ajo.ref"),v=Symbol.for("ajo.cache"),T=new Set(["nodeName","children","key","skip","memo","ref"]),W=t=>t.children,B=(t,n,...e)=>((n??(n={})).nodeName=t,!("children"in n)&&e.length&&(n.children=e.length==1?e[0]:e),n),S=(t,n,e=n.firstChild,i)=>{for(t of O(t)){let o=e;if(typeof t=="string"){for(;o&&o.nodeType!=3;)o=o.nextSibling;o?o.data!=t&&(o.data=t):o=document.createTextNode(t)}else if(t instanceof Node)o=t;else{for(;o&&!(o.localName==t.nodeName&&(o[b]??(o[b]=t.key))==t.key);)o=o.nextSibling;if(o??(o=Object.assign(document.createElementNS(t.xmlns??n.namespaceURI,t.nodeName),{[b]:t.key})),t.memo==null||R(o[k],o[k]=t.memo)){let s,m={},w=o[v]??Array.from(o.attributes).reduce((r,N)=>(r[N.name]=N.value,r),{});for(const r of Object.keys(Object.assign({},w,t)))T.has(r)||w[r]===(s=m[r]=t[r])||(r.startsWith("set:")?o[r.slice(4)]=s:s==null||s===!1?o.removeAttribute(r):o.setAttribute(r,s===!0?"":s));o[v]=m,t.skip||S(t.children,o),typeof t.ref=="function"&&(o[p]=t.ref)(o)}}o===e?e=e.nextSibling:I(n,o,e)}for(;e&&e!=i;){const o=e.nextSibling;e.nodeType==1&&C(e),n.removeChild(e),e=o}},R=(t,n)=>Array.isArray(t)&&Array.isArray(n)?t.some((e,i)=>e!==n[i]):t!==n,O=function*(t,n={h:""},e=!0){for(t of Array.isArray(t)?t.flat(1/0):[t])if(!(t==null||typeof t=="boolean"))if(typeof t=="object"&&"nodeName"in t)if(n.h&&(yield n.h,n.h=""),typeof t.nodeName=="function")if(t.nodeName.constructor.name=="GeneratorFunction"){const i=Object.assign({},t.nodeName.attrs),o=Object.assign({},t.nodeName.args);for(const s in t){const m=t[s];s.startsWith("attr:")?i[s.slice(5)]=m:s=="key"||s=="memo"||s.startsWith("set:")?i[s]=m:o[s]=m}i.nodeName=t.nodeName.is??"div",i.skip=!0,i.ref=P.bind(null,t.nodeName,o),yield i}else yield*O(t.nodeName(t),n,!1);else yield t;else n.h+=t;e&&n.h&&(yield n.h)},I=(t,n,e)=>{if(n.contains(document.activeElement)){const i=n.nextSibling;for(;e&&e!=n;){const o=e.nextSibling;t.insertBefore(e,i),e=o}}else t.insertBefore(n,e)},C=t=>{var n;for(const e of t.children)C(e);(n=t[p])==null||n.call(t,null)},g=Symbol.for("ajo.generator"),f=Symbol.for("ajo.iterator"),l=Symbol.for("ajo.args"),d=Symbol.for("ajo.context"),y=Symbol.for("ajo.effects"),c=Symbol.for("ajo.disposers"),h=Symbol.for("ajo.cleanups"),P=(t,n,e)=>{if(!e)return;e[g]??(e[g]=(Object.assign(e,G),e[d]=Object.create((a==null?void 0:a[d])??null),t));const{skip:i,ref:o,...s}=n;e[p]=U.bind(null,o,e),Object.assign(e[l]??(e[l]={}),s),i||e.next()},U=(t,n,e)=>{typeof t=="function"&&t(e),e||n.return()},M=t=>t.isConnected&&t.render(),u=(t,n=y)=>{var e;if(n==y)for(const i of t.children)u(i,n);if((e=t[n])!=null&&e.size)for(const i of t[n]){t[n].delete(i);try{const o=i();n==y&&typeof o=="function"&&(t[c]??(t[c]=new Set)).add(o)}catch(o){t.throw(o)}}},x=(t,n)=>{for(const e of t.set)n(e);t.set.clear(),t.queued=!1},z=()=>queueMicrotask.bind(null,x.bind(null,z,M)),E=()=>requestAnimationFrame.bind(null,x.bind(null,E,M)),F=()=>{const t=new MessageChannel;return t.port1.onmessage=x.bind(null,F,u),t.port2.postMessage.bind(t.port2,null)},j=(t,n)=>{t.queued||((t.run??(t.run=t()))(),t.queued=!0);const e=t.set??(t.set=new Set);for(const i of e){if(i.contains(n))return;n.contains(i)&&e.delete(i)}e.add(n)},A=(t,n,e)=>{if(typeof e=="function")return(t[n]??(t[n]=new Set)).add(e),()=>t[n].delete(e)};let a=null;const G={*[Symbol.iterator](){for(;;)yield this[l]},render(){a!=null&&a.contains(this)||this.next()},queueMicrotask(){j(z,this)},requestAnimationFrame(){j(E,this)},effect(t){return A(this,y,t)},cleanup(t){return A(this,h,t)},next(){var n,e,i;const t=a;a=this;try{(n=this[c])!=null&&n.size&&u(this,c);const o=(this[f]??(this[f]=this[g].call(this,this[l]))).next();(e=this[y])!=null&&e.size&&j(F,this),S(o.value,this),(i=this[p])==null||i.call(this,this),o.done&&this.return()}catch(o){this.throw(o)}finally{a=t}},throw(t){var n;for(let e=this;e;e=e.parentNode)if(typeof((n=e[f])==null?void 0:n.throw)=="function")try{return S(e[f].throw(t).value,e)}catch(i){t=new Error(i instanceof Error?i.message:i,{cause:t})}throw t},return(){var t,n,e;try{(t=this[c])!=null&&t.size&&u(this,c),(n=this[h])!=null&&n.size&&u(this,h),(e=this[f])==null||e.return()}catch(i){this.throw(i)}finally{this[f]=null}}},H=(t,n=Symbol())=>function(...e){const i=this??a;return i?e.length==0?n in i[d]?i[d][n]:t:i[d][n]=e[0]:t};exports.Fragment=W;exports.context=H;exports.h=B;exports.render=S;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const c=require("./context.cjs"),a=Symbol.for("ajo.key"),b=Symbol.for("ajo.memo"),y=Symbol.for("ajo.ref"),S=Symbol.for("ajo.cache"),f=Symbol.for("ajo.generator"),o=Symbol.for("ajo.iterator"),u=Symbol.for("ajo.args"),p=t=>t.children,A=(t,r,...n)=>((r??(r={})).nodeName=t,!("children"in r)&&n.length&&(r.children=n.length==1?n[0]:n),r),l=(t,r,n=r.firstChild,s)=>{for(t of m(t)){const e=C(t,r,n);e===n?n=n.nextSibling:F(r,e,n)}for(;n&&n!=s;){const e=n.nextSibling;n.nodeType==1&&j(n),r.removeChild(n),n=e}},m=function*(t,r={h:""},n=!0){if(!(t==null||typeof t=="boolean")){if(Array.isArray(t))for(t of t.flat(1/0))yield*m(t,r,!1);else typeof t=="object"&&"nodeName"in t?(r.h&&(yield r.h,r.h=""),typeof t.nodeName=="function"?yield*v(t,r):yield t):r.h+=t;n&&r.h&&(yield r.h)}},v=function*({nodeName:t,...r},n){t.constructor.name=="GeneratorFunction"?yield h(t,r):yield*m(t(r),n,!1)},h=function(t,r){const n={...t.attrs},s={...t.args};for(const e in r)e.startsWith("attr:")?n[e.slice(5)]=r[e]:e=="key"||e=="memo"||e.startsWith("set:")?n[e]=r[e]:s[e]=r[e];return{...n,nodeName:t.is??"div",skip:!0,ref:W.bind(null,t,s)}},C=(t,r,n)=>typeof t=="string"?N(t,n):O(t,r,n),N=(t,r)=>{for(;r&&r.nodeType!=3;)r=r.nextSibling;return r?r.data!=t&&(r.data=t):r=document.createTextNode(t),r},O=({nodeName:t,children:r,key:n,skip:s,memo:e,ref:g,...x},w,i)=>{for(;i&&!(i.localName==t&&(i[a]??(i[a]=n))==n);)i=i.nextSibling;return i??(i=Object.assign(document.createElementNS(x.xmlns??w.namespaceURI,t),{[a]:n})),(e==null||E(i[b],i[b]=e))&&(k(i[S]??T(i),i[S]=x,i),s||l(r,i),typeof g=="function"&&(i[y]=g)(i)),i},k=(t,r,n)=>{for(const s in{...t,...r})t[s]!==r[s]&&(s.startsWith("set:")?n[s.slice(4)]=r[s]:r[s]==null||r[s]===!1?n.removeAttribute(s):n.setAttribute(s,r[s]===!0?"":r[s]))},E=(t,r)=>Array.isArray(t)&&Array.isArray(r)?t.some((n,s)=>n!==r[s]):t!==r,T=t=>Array.from(t.attributes).reduce((r,n)=>(r[n.name]=n.value,r),{}),F=(t,r,n)=>{if(!r.contains(document.activeElement))return t.insertBefore(r,n);const s=r.nextSibling;for(;n&&n!=r;){const e=n.nextSibling;t.insertBefore(n,s),n=e}},j=t=>{var r;for(const n of t.children)j(n);(r=t[y])==null||r.call(t,null)},W=(t,{skip:r,ref:n,...s},e)=>{e&&(e[f]??(e[f]=(B(e),t)),e[y]=M.bind(null,n,e),Object.assign(e[u]??(e[u]={}),s),r||e.next())},B=t=>{var r;Object.assign(t,R),t[c.Context]=Object.create(((r=c.current())==null?void 0:r[c.Context])??null)},M=(t,r,n)=>{typeof t=="function"&&t(n),n||r.return()},R={render(){var t;(t=c.current())!=null&&t.contains(this)||this.next()},next(){var r;const t=c.current();c.current(this);try{const{value:n,done:s}=(this[o]??(this[o]=this[f].call(this,this[u]))).next();l(n,this),(r=this[y])==null||r.call(this,this),s&&this.return()}catch(n){this.throw(n)}finally{c.current(t)}},throw(t){var r;for(let n=this;n;n=n.parentNode)if(typeof((r=n[o])==null?void 0:r.throw)=="function")try{return l(n[o].throw(t).value,n)}catch(s){t=new Error(s instanceof Error?s.message:s,{cause:t})}throw t},return(){var t;try{(t=this[o])==null||t.return()}catch(r){this.throw(r)}finally{this[o]=null}}};exports.Fragment=p;exports.h=A;exports.render=l;
package/dist/index.js CHANGED
@@ -1,151 +1,93 @@
1
- const b = Symbol.for("ajo.key"), k = Symbol.for("ajo.memo"), S = Symbol.for("ajo.ref"), A = Symbol.for("ajo.cache"), T = /* @__PURE__ */ new Set(["nodeName", "children", "key", "skip", "memo", "ref"]), G = (t) => t.children, H = (t, e, ...n) => ((e ?? (e = {})).nodeName = t, !("children" in e) && n.length && (e.children = n.length == 1 ? n[0] : n), e), x = (t, e, n = e.firstChild, i) => {
2
- for (t of O(t)) {
3
- let o = n;
4
- if (typeof t == "string") {
5
- for (; o && o.nodeType != 3; ) o = o.nextSibling;
6
- o ? o.data != t && (o.data = t) : o = document.createTextNode(t);
7
- } else if (t instanceof Node)
8
- o = t;
9
- else {
10
- for (; o && !(o.localName == t.nodeName && (o[b] ?? (o[b] = t.key)) == t.key); ) o = o.nextSibling;
11
- if (o ?? (o = Object.assign(document.createElementNS(t.xmlns ?? e.namespaceURI, t.nodeName), { [b]: t.key })), t.memo == null || W(o[k], o[k] = t.memo)) {
12
- let s, m = {}, w = o[A] ?? Array.from(o.attributes).reduce((r, N) => (r[N.name] = N.value, r), {});
13
- for (const r of Object.keys(Object.assign({}, w, t)))
14
- T.has(r) || w[r] === (s = m[r] = t[r]) || (r.startsWith("set:") ? o[r.slice(4)] = s : s == null || s === !1 ? o.removeAttribute(r) : o.setAttribute(r, s === !0 ? "" : s));
15
- o[A] = m, t.skip || x(t.children, o), typeof t.ref == "function" && (o[S] = t.ref)(o);
16
- }
17
- }
18
- o === n ? n = n.nextSibling : B(e, o, n);
1
+ import { Context as b, current as a } from "./context.js";
2
+ const c = Symbol.for("ajo.key"), S = Symbol.for("ajo.memo"), l = Symbol.for("ajo.ref"), j = Symbol.for("ajo.cache"), f = Symbol.for("ajo.generator"), o = Symbol.for("ajo.iterator"), u = Symbol.for("ajo.args"), I = (t) => t.children, M = (t, r, ...n) => ((r ?? (r = {})).nodeName = t, !("children" in r) && n.length && (r.children = n.length == 1 ? n[0] : n), r), y = (t, r, n = r.firstChild, s) => {
3
+ for (t of m(t)) {
4
+ const e = h(t, r, n);
5
+ e === n ? n = n.nextSibling : T(r, e, n);
19
6
  }
20
- for (; n && n != i; ) {
21
- const o = n.nextSibling;
22
- n.nodeType == 1 && C(n), e.removeChild(n), n = o;
7
+ for (; n && n != s; ) {
8
+ const e = n.nextSibling;
9
+ n.nodeType == 1 && w(n), r.removeChild(n), n = e;
23
10
  }
24
- }, W = (t, e) => Array.isArray(t) && Array.isArray(e) ? t.some((n, i) => n !== e[i]) : t !== e, O = function* (t, e = { h: "" }, n = !0) {
25
- for (t of Array.isArray(t) ? t.flat(1 / 0) : [t])
26
- if (!(t == null || typeof t == "boolean"))
27
- if (typeof t == "object" && "nodeName" in t)
28
- if (e.h && (yield e.h, e.h = ""), typeof t.nodeName == "function")
29
- if (t.nodeName.constructor.name == "GeneratorFunction") {
30
- const i = Object.assign({}, t.nodeName.attrs), o = Object.assign({}, t.nodeName.args);
31
- for (const s in t) {
32
- const m = t[s];
33
- s.startsWith("attr:") ? i[s.slice(5)] = m : s == "key" || s == "memo" || s.startsWith("set:") ? i[s] = m : o[s] = m;
34
- }
35
- i.nodeName = t.nodeName.is ?? "div", i.skip = !0, i.ref = R.bind(null, t.nodeName, o), yield i;
36
- } else yield* O(t.nodeName(t), e, !1);
37
- else yield t;
38
- else e.h += t;
39
- n && e.h && (yield e.h);
40
- }, B = (t, e, n) => {
41
- if (e.contains(document.activeElement)) {
42
- const i = e.nextSibling;
43
- for (; n && n != e; ) {
44
- const o = n.nextSibling;
45
- t.insertBefore(n, i), n = o;
46
- }
47
- } else t.insertBefore(e, n);
48
- }, C = (t) => {
49
- var e;
50
- for (const n of t.children) C(n);
51
- (e = t[S]) == null || e.call(t, null);
52
- }, g = Symbol.for("ajo.generator"), f = Symbol.for("ajo.iterator"), l = Symbol.for("ajo.args"), d = Symbol.for("ajo.context"), y = Symbol.for("ajo.effects"), c = Symbol.for("ajo.disposers"), p = Symbol.for("ajo.cleanups"), R = (t, e, n) => {
53
- if (!n) return;
54
- n[g] ?? (n[g] = (Object.assign(n, U), n[d] = Object.create((a == null ? void 0 : a[d]) ?? null), t));
55
- const { skip: i, ref: o, ...s } = e;
56
- n[S] = I.bind(null, o, n), Object.assign(n[l] ?? (n[l] = {}), s), i || n.next();
57
- }, I = (t, e, n) => {
58
- typeof t == "function" && t(n), n || e.return();
59
- }, z = (t) => t.isConnected && t.render(), u = (t, e = y) => {
60
- var n;
61
- if (e == y) for (const i of t.children) u(i, e);
62
- if ((n = t[e]) != null && n.size) for (const i of t[e]) {
63
- t[e].delete(i);
64
- try {
65
- const o = i();
66
- e == y && typeof o == "function" && (t[c] ?? (t[c] = /* @__PURE__ */ new Set())).add(o);
67
- } catch (o) {
68
- t.throw(o);
69
- }
11
+ }, m = function* (t, r = { h: "" }, n = !0) {
12
+ if (!(t == null || typeof t == "boolean")) {
13
+ if (Array.isArray(t)) for (t of t.flat(1 / 0)) yield* m(t, r, !1);
14
+ else typeof t == "object" && "nodeName" in t ? (r.h && (yield r.h, r.h = ""), typeof t.nodeName == "function" ? yield* A(t, r) : yield t) : r.h += t;
15
+ n && r.h && (yield r.h);
70
16
  }
71
- }, j = (t, e) => {
72
- for (const n of t.set) e(n);
73
- t.set.clear(), t.queued = !1;
74
- }, E = () => queueMicrotask.bind(null, j.bind(null, E, z)), M = () => requestAnimationFrame.bind(null, j.bind(null, M, z)), F = () => {
75
- const t = new MessageChannel();
76
- return t.port1.onmessage = j.bind(null, F, u), t.port2.postMessage.bind(t.port2, null);
77
- }, h = (t, e) => {
78
- t.queued || ((t.run ?? (t.run = t()))(), t.queued = !0);
79
- const n = t.set ?? (t.set = /* @__PURE__ */ new Set());
80
- for (const i of n) {
81
- if (i.contains(e)) return;
82
- e.contains(i) && n.delete(i);
17
+ }, A = function* ({ nodeName: t, ...r }, n) {
18
+ t.constructor.name == "GeneratorFunction" ? yield v(t, r) : yield* m(t(r), n, !1);
19
+ }, v = function(t, r) {
20
+ const n = { ...t.attrs }, s = { ...t.args };
21
+ for (const e in r)
22
+ e.startsWith("attr:") ? n[e.slice(5)] = r[e] : e == "key" || e == "memo" || e.startsWith("set:") ? n[e] = r[e] : s[e] = r[e];
23
+ return { ...n, nodeName: t.is ?? "div", skip: !0, ref: W.bind(null, t, s) };
24
+ }, h = (t, r, n) => typeof t == "string" ? N(t, n) : k(t, r, n), N = (t, r) => {
25
+ for (; r && r.nodeType != 3; ) r = r.nextSibling;
26
+ return r ? r.data != t && (r.data = t) : r = document.createTextNode(t), r;
27
+ }, k = ({ nodeName: t, children: r, key: n, skip: s, memo: e, ref: x, ...g }, p, i) => {
28
+ for (; i && !(i.localName == t && (i[c] ?? (i[c] = n)) == n); ) i = i.nextSibling;
29
+ return i ?? (i = Object.assign(document.createElementNS(g.xmlns ?? p.namespaceURI, t), { [c]: n })), (e == null || E(i[S], i[S] = e)) && (C(i[j] ?? O(i), i[j] = g, i), s || y(r, i), typeof x == "function" && (i[l] = x)(i)), i;
30
+ }, C = (t, r, n) => {
31
+ for (const s in { ...t, ...r })
32
+ t[s] !== r[s] && (s.startsWith("set:") ? n[s.slice(4)] = r[s] : r[s] == null || r[s] === !1 ? n.removeAttribute(s) : n.setAttribute(s, r[s] === !0 ? "" : r[s]));
33
+ }, E = (t, r) => Array.isArray(t) && Array.isArray(r) ? t.some((n, s) => n !== r[s]) : t !== r, O = (t) => Array.from(t.attributes).reduce((r, n) => (r[n.name] = n.value, r), {}), T = (t, r, n) => {
34
+ if (!r.contains(document.activeElement)) return t.insertBefore(r, n);
35
+ const s = r.nextSibling;
36
+ for (; n && n != r; ) {
37
+ const e = n.nextSibling;
38
+ t.insertBefore(n, s), n = e;
83
39
  }
84
- n.add(e);
85
- }, v = (t, e, n) => {
86
- if (typeof n == "function")
87
- return (t[e] ?? (t[e] = /* @__PURE__ */ new Set())).add(n), () => t[e].delete(n);
88
- };
89
- let a = null;
90
- const U = {
91
- *[Symbol.iterator]() {
92
- for (; ; ) yield this[l];
93
- },
40
+ }, w = (t) => {
41
+ var r;
42
+ for (const n of t.children) w(n);
43
+ (r = t[l]) == null || r.call(t, null);
44
+ }, W = (t, { skip: r, ref: n, ...s }, e) => {
45
+ e && (e[f] ?? (e[f] = (B(e), t)), e[l] = F.bind(null, n, e), Object.assign(e[u] ?? (e[u] = {}), s), r || e.next());
46
+ }, B = (t) => {
47
+ var r;
48
+ Object.assign(t, R), t[b] = Object.create(((r = a()) == null ? void 0 : r[b]) ?? null);
49
+ }, F = (t, r, n) => {
50
+ typeof t == "function" && t(n), n || r.return();
51
+ }, R = {
94
52
  render() {
95
- a != null && a.contains(this) || this.next();
96
- },
97
- queueMicrotask() {
98
- h(E, this);
99
- },
100
- requestAnimationFrame() {
101
- h(M, this);
102
- },
103
- effect(t) {
104
- return v(this, y, t);
105
- },
106
- cleanup(t) {
107
- return v(this, p, t);
53
+ var t;
54
+ (t = a()) != null && t.contains(this) || this.next();
108
55
  },
109
56
  next() {
110
- var e, n, i;
111
- const t = a;
112
- a = this;
57
+ var r;
58
+ const t = a();
59
+ a(this);
113
60
  try {
114
- (e = this[c]) != null && e.size && u(this, c);
115
- const o = (this[f] ?? (this[f] = this[g].call(this, this[l]))).next();
116
- (n = this[y]) != null && n.size && h(F, this), x(o.value, this), (i = this[S]) == null || i.call(this, this), o.done && this.return();
117
- } catch (o) {
118
- this.throw(o);
61
+ const { value: n, done: s } = (this[o] ?? (this[o] = this[f].call(this, this[u]))).next();
62
+ y(n, this), (r = this[l]) == null || r.call(this, this), s && this.return();
63
+ } catch (n) {
64
+ this.throw(n);
119
65
  } finally {
120
- a = t;
66
+ a(t);
121
67
  }
122
68
  },
123
69
  throw(t) {
124
- var e;
125
- for (let n = this; n; n = n.parentNode) if (typeof ((e = n[f]) == null ? void 0 : e.throw) == "function") try {
126
- return x(n[f].throw(t).value, n);
127
- } catch (i) {
128
- t = new Error(i instanceof Error ? i.message : i, { cause: t });
70
+ var r;
71
+ for (let n = this; n; n = n.parentNode) if (typeof ((r = n[o]) == null ? void 0 : r.throw) == "function") try {
72
+ return y(n[o].throw(t).value, n);
73
+ } catch (s) {
74
+ t = new Error(s instanceof Error ? s.message : s, { cause: t });
129
75
  }
130
76
  throw t;
131
77
  },
132
78
  return() {
133
- var t, e, n;
79
+ var t;
134
80
  try {
135
- (t = this[c]) != null && t.size && u(this, c), (e = this[p]) != null && e.size && u(this, p), (n = this[f]) == null || n.return();
136
- } catch (i) {
137
- this.throw(i);
81
+ (t = this[o]) == null || t.return();
82
+ } catch (r) {
83
+ this.throw(r);
138
84
  } finally {
139
- this[f] = null;
85
+ this[o] = null;
140
86
  }
141
87
  }
142
- }, J = (t, e = Symbol()) => function(...n) {
143
- const i = this ?? a;
144
- return i ? n.length == 0 ? e in i[d] ? i[d][e] : t : i[d][e] = n[0] : t;
145
88
  };
146
89
  export {
147
- G as Fragment,
148
- J as context,
149
- H as h,
150
- x as render
90
+ I as Fragment,
91
+ M as h,
92
+ y as render
151
93
  };
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("./index.cjs"),d=require("./html.cjs"),h=async function*(n){const t=new Set,e=[],s=new Map,l=(i="")=>{const r=s.get(i)??(s.set(i,0),0);return s.set(i,r+1),i?`${i}:${r}`:String(r)},c=i=>{const r=Promise.resolve(`<script>window.$stream?.push(${JSON.stringify(i)})<\/script>`);t.add(r),r.then(a=>e.push(a)).finally(()=>t.delete(r))};for(n of d.html(n,l,c))yield n;for(;t.size;)for(await Promise.race(t);e.length;)yield e.shift();for(;e.length;)yield e.shift()},u=async({id:n,src:t,h:e})=>{const s=document.querySelector(`[data-ssr="${n}"]`);s&&(t?o.render(o.h((await import(t)).default,e),s):o.render(e,s))};exports.hydrate=u;exports.stream=h;
package/dist/stream.js ADDED
@@ -0,0 +1,22 @@
1
+ import { render as n, h as d } from "./index.js";
2
+ import { html as h } from "./html.js";
3
+ const u = async function* (r) {
4
+ const e = /* @__PURE__ */ new Set(), t = [], s = /* @__PURE__ */ new Map(), l = (i = "") => {
5
+ const o = s.get(i) ?? (s.set(i, 0), 0);
6
+ return s.set(i, o + 1), i ? `${i}:${o}` : String(o);
7
+ }, c = (i) => {
8
+ const o = Promise.resolve(`<script>window.$stream?.push(${JSON.stringify(i)})<\/script>`);
9
+ e.add(o), o.then((a) => t.push(a)).finally(() => e.delete(o));
10
+ };
11
+ for (r of h(r, l, c)) yield r;
12
+ for (; e.size; )
13
+ for (await Promise.race(e); t.length; ) yield t.shift();
14
+ for (; t.length; ) yield t.shift();
15
+ }, w = async ({ id: r, src: e, h: t }) => {
16
+ const s = document.querySelector(`[data-ssr="${r}"]`);
17
+ s && (e ? n(d((await import(e)).default, t), s) : n(t, s));
18
+ };
19
+ export {
20
+ w as hydrate,
21
+ u as stream
22
+ };
package/package.json CHANGED
@@ -1,11 +1,8 @@
1
1
  {
2
2
  "name": "ajo",
3
- "version": "0.1.22",
3
+ "version": "0.1.23",
4
4
  "description": "ajo is a JavaScript view library for building user interfaces",
5
5
  "type": "module",
6
- "module": "./dist/index.js",
7
- "main": "./dist/index.cjs",
8
- "types": "./types.ts",
9
6
  "exports": {
10
7
  ".": {
11
8
  "types": "./types.ts",
@@ -16,18 +13,34 @@
16
13
  "types": "./types.ts",
17
14
  "import": "./dist/html.js",
18
15
  "require": "./dist/html.cjs"
16
+ },
17
+ "./context": {
18
+ "types": "./types.ts",
19
+ "import": "./dist/context.js",
20
+ "require": "./dist/context.cjs"
21
+ },
22
+ "./stream": {
23
+ "types": "./types.ts",
24
+ "import": "./dist/stream.js",
25
+ "require": "./dist/stream.cjs"
19
26
  }
20
27
  },
21
28
  "files": [
22
29
  "dist",
23
30
  "types.ts"
24
31
  ],
32
+ "scripts": {
33
+ "test": "vitest --run",
34
+ "build": "vite build",
35
+ "bump": "pnpm version patch && git push && git push --tags",
36
+ "release": "pnpm test && pnpm build && pnpm bump && pnpm publish"
37
+ },
25
38
  "devDependencies": {
26
- "@types/node": "22.15.29",
27
- "happy-dom": "17.6.1",
39
+ "@types/node": "24.0.0",
40
+ "happy-dom": "18.0.1",
28
41
  "vite": "6.3.5",
29
42
  "vite-tsconfig-paths": "5.1.4",
30
- "vitest": "3.2.0"
43
+ "vitest": "3.2.3"
31
44
  },
32
45
  "keywords": [
33
46
  "ui",
@@ -40,11 +53,5 @@
40
53
  "author": "Cristian Falcone",
41
54
  "license": "ISC",
42
55
  "bugs": "https://github.com/cristianfalcone/ajo/issues",
43
- "homepage": "https://github.com/cristianfalcone/ajo#readme",
44
- "scripts": {
45
- "test": "vitest --run",
46
- "build": "vite build",
47
- "bump": "pnpm version patch && git push && git push --tags",
48
- "release": "pnpm test && pnpm build && pnpm bump && pnpm publish"
49
- }
50
- }
56
+ "homepage": "https://github.com/cristianfalcone/ajo#readme"
57
+ }
package/readme.md CHANGED
@@ -23,444 +23,6 @@ Ajo is a lightweight and efficient JavaScript library for building dynamic user
23
23
  - Lightweight design
24
24
  - Server-Side Rendering (SSR) support
25
25
 
26
- ## Quick Start
27
-
28
- ### Installation
29
-
30
- ```bash
31
- npm install ajo
32
- ```
33
-
34
- ### Basic Usage
35
-
36
- ```jsx
37
- /** @jsx h */
38
- import { h, render } from 'ajo'
39
-
40
- const Greeting = ({ name }) => <h1>Hello, {name}!</h1>
41
-
42
- function* Counter() {
43
-
44
- let count = 0
45
-
46
- const increment = () => {
47
- count++
48
- this.render()
49
- }
50
-
51
- while (true) yield (
52
- <>
53
- <p>Count: {count}</p>
54
- <button set:onclick={increment}>Increment</button>
55
- </>
56
- )
57
- }
58
-
59
- function* App() {
60
- while (true) yield (
61
- <>
62
- <Greeting name="Ajo Developer" />
63
- <Counter />
64
- </>
65
- )
66
- }
67
-
68
- render(<App />, document.body)
69
- ```
70
-
71
- ## Core Concepts
72
-
73
- ### HTML Attributes vs DOM Properties
74
-
75
- Ajo distinguishes between HTML attributes and DOM properties. Use regular attributes for HTML attributes, and the `set:` prefix to set DOM properties directly:
76
-
77
- ```jsx
78
- <input
79
- type="text"
80
- id="username"
81
- class="form-input"
82
- placeholder="Enter username"
83
- set:value={inputValue}
84
- set:onclick={handleClick}
85
- set:disabled={isDisabled}
86
- />
87
- ```
88
-
89
- In this example:
90
- - `type`, `id`, `class`, and `placeholder` are regular HTML attributes.
91
- - `set:value`, `set:onclick`, and `set:disabled` are DOM properties set directly on the element.
92
-
93
- ### Special Attributes
94
-
95
- Ajo uses special attributes for optimization and control:
96
- - `key`: For efficient list rendering
97
- - `skip`: To prevent rendering of child elements
98
- - `memo`: For memoization of components or elements
99
- - `ref`: To get references to DOM nodes or component instances
100
-
101
- ```jsx
102
- <TodoItem
103
- key={todo.id}
104
- memo={[todo.completed]}
105
- ref={el => el ? todosRefs.add(el) : todosRefs.delete(todo.id)}
106
- >
107
- <TodoTitle>{todo.title}</TodoTitle>
108
- <div set:innerHTML={marked(todo.content)} skip></div>
109
- </TodoItem>
110
- ```
111
-
112
- ### Stateless and Stateful Components
113
-
114
- Stateless components are simple functions:
115
-
116
- ```jsx
117
- const Greeting = ({ name }) => <h1>Hello, {name}!</h1>
118
- ```
119
-
120
- Stateful components use generator functions:
121
-
122
- ```jsx
123
- function* Counter() {
124
-
125
- let count = 0
126
-
127
- while (true) yield (
128
- <button set:onclick={() => { count++; this.render(); }}>
129
- {count}
130
- </button>
131
- )
132
- }
133
- ```
134
-
135
- State handling in Ajo is straightforward:
136
- - State is managed using regular variables within the generator function.
137
- - The `this.render()` method triggers a re-render when state changes.
138
- - The `this.cleanup()` method is used to clean up resources when the component unmounts.
139
- - Each iteration of the generator function represents a new render cycle.
140
-
141
- ```jsx
142
- function* Timer() {
143
-
144
- let seconds = 0
145
-
146
- const intervalId = setInterval(() => {
147
- seconds++
148
- this.render() // Trigger a re-render
149
- }, 1000)
150
-
151
- this.cleanup(() => clearInterval(intervalId)) // Cleanup
152
-
153
- while (true) yield <div>Seconds: {seconds}</div>
154
- }
155
- ```
156
-
157
- ### Component Lifecycle
158
-
159
- Stateful components have a simple lifecycle pattern:
160
- - After mount: Component's element is mounted and the generator function is called. You can initialize resources here.
161
- - Before render: Before component's children render
162
- - After render: After component's children render
163
- - Before re-render: Before component's children re-render
164
- - Before unmount: Component's element is about to be unmounted. You can clean up resources here.
165
-
166
- ```jsx
167
- function* LifecycleDemo() {
168
-
169
- console.log('After mount')
170
-
171
- this.cleanup(() => console.log('Before unmount'))
172
-
173
- while (true) {
174
-
175
- console.log('Before render')
176
-
177
- this.effect(() => console.log('After render'))
178
-
179
- yield <div>Hello, Ajo!</div>
180
-
181
- console.log('Before re-render')
182
- }
183
- }
184
- ```
185
-
186
- ### attr: Attributes
187
-
188
- Use the `attr:` prefix to add HTML attributes to a component's root element:
189
-
190
- ```jsx
191
- function* CustomButton(props) {
192
- while (true) yield <>{props.children}</>
193
- }
194
- CustomButton.is = 'button'
195
-
196
- // Usage
197
- <CustomButton attr:class="primary" attr:id="submit-btn">
198
- Click me
199
- </CustomButton>
200
- ```
201
-
202
- ### Component.attrs, Component.args and Component.is
203
-
204
- Use `Component.attrs` to set default attributes for a component:
205
-
206
- ```jsx
207
- function* CustomButton(args) {
208
- while (true) yield <>{props.children}</>
209
- }
210
- CustomButton.attrs = { class: 'btn btn-primary' }
211
- ```
212
-
213
- Use `Component.args` to set default arguments for a component:
214
-
215
- ```jsx
216
- function* Greeting(args) {
217
- while (true) yield <>Hello, {props.name}!</>
218
- }
219
- Greeting.args = { name: 'Guest' }
220
- ```
221
-
222
- Use `Component.is` to specify the HTML element for a component:
223
-
224
- ```jsx
225
- function* CustomInput(args) {
226
- while (true) yield <>{props.children}</>
227
- }
228
- CustomInput.is = 'input'
229
- ```
230
-
231
- ## API
232
-
233
- ### `ajo` module
234
-
235
- #### `h(type, props?, ...children)`
236
-
237
- Creates virtual DOM elements.
238
-
239
- ```javascript
240
- const element = h('div', { class: 'container' }, 'Hello, Ajo!')
241
- ```
242
-
243
- #### `render(vnode, container)`
244
-
245
- Renders a virtual DOM tree into a DOM element.
246
-
247
- ```javascript
248
- render(h(App), document.body)
249
- ```
250
-
251
- #### `Fragment({ children })`
252
-
253
- A component for grouping elements without adding extra nodes to the DOM.
254
-
255
- ```jsx
256
- const List = () => (
257
- <Fragment>
258
- <li>Item 1</li>
259
- <li>Item 2</li>
260
- </Fragment>
261
- )
262
- ```
263
-
264
- #### `context(defaultValue)`
265
-
266
- Creates a context with an optional default value.
267
-
268
- ```javascript
269
- const ThemeContext = context('light')
270
-
271
- // In a stateless component:
272
- const StatelessComponent = () => {
273
- const theme = ThemeContext()
274
- return <div>Current theme: {theme}</div>
275
- }
276
-
277
- // In a stateful component:
278
- function* StatefulComponent() {
279
- while (true) {
280
- const theme = ThemeContext()
281
- yield <div>Current theme: {theme}</div>
282
- }
283
- }
284
-
285
- // Setting context value:
286
- function* App() {
287
-
288
- ThemeContext('dark')
289
-
290
- while (true) {
291
- yield (
292
- <>
293
- <StatelessComponent />
294
- <StatefulComponent />
295
- </>
296
- )
297
- }
298
- }
299
- ```
300
-
301
- #### Stateful Components and Component Methods
302
-
303
- Ajo's stateful components are implemented as generator functions and have access to several special methods:
304
-
305
- ```javascript
306
- function* StatefulComponent(props) {
307
- // Component logic here
308
- while (true) {
309
- yield (/* JSX */)
310
- }
311
- }
312
- ```
313
-
314
- Component methods:
315
-
316
- - `this.render()`: Triggers a re-render of the component. It's the primary method for updating the component's UI after state changes.
317
-
318
- ```javascript
319
- function* Counter() {
320
-
321
- let count = 0
322
-
323
- const increment = () => {
324
- count++
325
- this.render() // Re-render to reflect the new count
326
- }
327
-
328
- while (true) {
329
- yield <button set:onclick={increment}>{count}</button>
330
- }
331
- }
332
- ```
333
-
334
- - `this.queueMicrotasks()`: Schedules an asynchronous re-render of the component. This is useful for batching multiple updates together for better performance.
335
-
336
- ```javascript
337
- function* AsyncCounter() {
338
-
339
- let count = 0
340
-
341
- const increment = () => {
342
- count++
343
- this.queueMicrotask() // Schedule an asynchronous re-render
344
- }
345
-
346
- while (true) {
347
- yield <button set:onclick={increment}>{count}</button>
348
- }
349
- }
350
- ```
351
-
352
- - `this.requestAnimationFrame()`: Similar to `this.queueMicrotask()`, but uses `requestAnimationFrame` for smooth animations.
353
-
354
- ```javascript
355
- function* AnimatedCounter() {
356
-
357
- let count = 0
358
-
359
- const increment = () => {
360
- count++
361
- this.requestAnimationFrame() // Schedule a re-render on the next animation frame
362
- }
363
-
364
- while (true) {
365
- yield <button set:onclick={increment}>{count}</button>
366
- }
367
- }
368
- ```
369
-
370
- - `this.cleanup(fn)`: Registers a callback to be executed when the component unmounts. This is useful for cleaning up resources or subscriptions.
371
-
372
- ```javascript
373
- function* TimerComponent() {
374
-
375
- let seconds = 0
376
-
377
- const timer = setInterval(() => {
378
- seconds++
379
- this.render()
380
- }, 1000)
381
-
382
- this.cleanup(() => clearInterval(timer)) // Clean up the timer when unmounting
383
-
384
- while (true) {
385
- yield <div>Seconds: {seconds}</div>
386
- }
387
- }
388
- ```
389
-
390
- - `this.effect(fn)`: Registers a callback to be run after the next render.
391
-
392
- ```javascript
393
- function* DataFetchingComponent() {
394
-
395
- let data = null
396
-
397
- this.effect(async () => {
398
- const response = await fetch('https://api.example.com/data')
399
- data = await response.json()
400
- this.render()
401
- })
402
-
403
- while (true) {
404
- yield data ? <div>{JSON.stringify(data)}</div> : <div>Loading...</div>
405
- }
406
- }
407
- ```
408
-
409
- If you want to execute an effect after each render, place it inside the main loop:
410
-
411
- ```javascript
412
- function* EffectfulComponent() {
413
-
414
- let count = 0
415
-
416
- while (true) {
417
-
418
- this.effect(() => console.log(`Count updated to ${count}`))
419
-
420
- yield <button set:onclick={() => { count++; this.render() }}>{count}</button>
421
-
422
- console.log(`Optionally you can clean up effect for count ${count} here`)
423
- }
424
- }
425
- ```
426
-
427
- - `this.next()`: Advances the generator to the next yield point. It's automatically called by `this.render()` and is rarely used directly.
428
-
429
- - `this.throw(error)`: Throws an error in the generator. Useful for error propagation and creating error boundaries.
430
-
431
- - `this.return()`: Completes the generator execution. It's automatically called when a component is unmounted, but can be used manually to reset a component's state.
432
-
433
- These methods provide powerful control over the component's lifecycle, state management, and side effects, allowing for efficient and flexible UI updates. Note that `this.throw()` and `this.return()` are often called automatically by Ajo in response to errors or component unmounting, respectively, but can also be used manually when needed.
434
-
435
- ### `ajo/html` module
436
-
437
- For server-side rendering:
438
-
439
- #### `render(vnode)`
440
-
441
- Renders a virtual DOM tree to an HTML string.
442
-
443
- ```javascript
444
- import { render } from 'ajo/html'
445
- import { App } from './components'
446
-
447
- const html = render(<App />)
448
- ```
449
-
450
- #### `html(vnode)`
451
-
452
- Generates an iterator of HTML strings for streaming.
453
-
454
- ```javascript
455
- import { html } from 'ajo/html'
456
- import { App } from './components'
457
-
458
- for (const chunk of html(<App />)) {
459
- // Send chunk to the client
460
- stream.write(chunk)
461
- }
462
- ```
463
-
464
26
  ## License
465
27
 
466
28
  ISC © [Cristian Falcone](cristianfalcone.com)
package/types.ts CHANGED
@@ -4,17 +4,18 @@ declare module 'ajo' {
4
4
 
5
5
  type Type = Tag | Stateless | Stateful
6
6
 
7
- type Component = Stateless | Stateful
7
+ type Component<TProps extends Props = {}> = Stateless<TProps> | Stateful<TProps>
8
8
 
9
9
  type Props = Record<string, unknown>
10
10
 
11
- type VNode<TTag extends Type, TProps extends Props = Props> = TProps & {
11
+ type VNode<TTag extends Type, TProps extends Props> = TProps & {
12
12
  nodeName: TTag,
13
+ children?: Children,
13
14
  }
14
15
 
15
16
  type Children = unknown
16
17
 
17
- type ElementType<TTag = Tag> = TTag extends keyof HTMLElementTagNameMap
18
+ type ElementType<TTag> = TTag extends keyof HTMLElementTagNameMap
18
19
  ? HTMLElementTagNameMap[TTag]
19
20
  : TTag extends keyof SVGElementTagNameMap
20
21
  ? SVGElementTagNameMap[TTag]
@@ -27,7 +28,7 @@ declare module 'ajo' {
27
28
  ref: (el: TElement | null) => void,
28
29
  } & ElementChildrenAttribute
29
30
 
30
- type PropSetter<TTag = Tag> = {
31
+ type PropSetter<TTag> = {
31
32
  [K in keyof ElementType<TTag> as `set:${Exclude<K, symbol>}`]: ElementType<TTag>[K]
32
33
  }
33
34
 
@@ -35,24 +36,19 @@ declare module 'ajo' {
35
36
  [key: `attr:${string}`]: unknown
36
37
  }
37
38
 
38
- type Stateless<TArguments extends Props = Props> = (args: TArguments) => Children
39
+ type Stateless<TArguments extends Props = {}> = (args: TArguments) => Children
39
40
 
40
- type Stateful<TArguments extends Props = Props, TTag extends Tag = 'div'> = {
41
- (this: StatefulElement<TArguments, TTag>, args: StatefulProps<TArguments, TTag>): Iterator<Children>
41
+ type Stateful<TArguments extends Props = {}, TTag extends Tag = 'div'> = {
42
+ (this: StatefulElement<TTag>, args: StatefulProps<TArguments, TTag>): Iterator<Children>
42
43
  } & (TTag extends 'div' ? { is?: TTag } : { is: TTag }) & { attrs?: Partial<PropSetter<TTag>> & Props, args?: Partial<TArguments> }
43
44
 
44
- type StatefulProps<TArguments extends Props = Props, TTag extends Tag = 'div'> =
45
- Partial<SpecialProps<StatefulElement<TArguments, TTag>> & PropSetter<TTag>> &
45
+ type StatefulProps<TArguments, TTag> =
46
+ Partial<SpecialProps<StatefulElement<TTag>> & PropSetter<TTag>> &
46
47
  AttrSetter &
47
48
  TArguments
48
49
 
49
- type StatefulElement<TArguments extends Props = Props, TTag extends Tag = Tag> = ElementType<TTag> & {
50
- [Symbol.iterator]: () => Iterator<TArguments>,
50
+ type StatefulElement<TTag> = ElementType<TTag> & {
51
51
  render: () => void,
52
- queueMicrotask: () => void,
53
- requestAnimationFrame: () => void,
54
- effect: (fn: () => void | (() => void)) => () => void,
55
- cleanup: (fn: () => void) => () => void,
56
52
  next: () => void,
57
53
  throw: (value?: unknown) => void,
58
54
  return: () => void,
@@ -67,12 +63,22 @@ declare module 'ajo' {
67
63
  function Fragment({ children }: ElementChildrenAttribute): typeof children
68
64
  function h(tag: Type, props?: Props | null, ...children: Children[]): VNode<Type, Props>
69
65
  function render(h: Children, el: Element, child?: Node, ref?: Node): void
70
- function context<T>(fallback?: T): (value?: T) => T
71
66
  }
72
67
 
73
68
  declare module 'ajo/html' {
69
+
70
+ type Patch = { id: string, h?: import('ajo').Children, src?: string, done?: boolean }
71
+
74
72
  function render(h: import('ajo').Children): string
75
- function html(h: import('ajo').Children): IterableIterator<string>
73
+ function html(h: import('ajo').Children, alloc?: (parentId: string) => string, push?: (patch: Patch) => void): IterableIterator<string>
74
+ }
75
+
76
+ declare module 'ajo/stream' {
77
+ function stream(h: import('ajo').Children): AsyncIterableIterator<string>
78
+ function hydrate(patch: import('ajo/html').Patch): Promise<void>
79
+ }
80
+
81
+ declare module 'ajo/context' {
76
82
  function context<T>(fallback?: T): (value?: T) => T
77
83
  }
78
84