ajo 0.1.33 → 0.1.34

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.
package/LLMs.md CHANGED
@@ -117,6 +117,7 @@ ref?.next() // trigger re-render from outside
117
117
  | **Re-render** | `this.next(fn?)` — returns `fn`'s result. Safe after unmount (no-op). Use `this.throw(e)` for explicit error routing |
118
118
  | **Context** | `context<T>(fallback)` creates context. Stateless: read only. Stateful: read/write. Reads go inside loop (fresh each render). Constant writes can go before loop; dynamic writes go inside loop |
119
119
  | **Lists** | Always provide unique `key` on elements |
120
+ | **SVG** | Set `xmlns="http://www.w3.org/2000/svg"` on the root `<svg>` element — children inherit the namespace |
120
121
  | **Refs** | `ref={el => ...}` on elements. Receives `null` on unmount. Stateful ref type: `ThisParameterType<typeof Component>` |
121
122
  | **Memo** | `memo={[deps]}` array, `memo={value}` single, or just `memo` (never re-render). Skips subtree if unchanged |
122
123
  | **Skip** | `skip` excludes children from reconciliation; it does not sanitize `set:innerHTML` |
@@ -261,7 +262,7 @@ className="..."
261
262
  onClick={...}
262
263
  useState, useEffect, useCallback
263
264
 
264
- // ❌ class/style as object or array: no special handling in Ajo
265
+ // ❌ class/style as object or array: no special handling in Ajo (compile error)
265
266
  <div class={{ active: isActive }} /> // won't work
266
267
  <div class={['btn', 'primary']} /> // won't work
267
268
  <div style={{ color: 'red' }} /> // won't work
@@ -337,10 +338,17 @@ pnpm add ajo
337
338
  yarn add ajo
338
339
  ```
339
340
 
340
- Configure JSX automatic runtime (Vite example):
341
+ Configure the JSX automatic runtime in the build tool:
341
342
 
342
343
  ```ts
343
- // vite.config.ts
344
+ // vite.config.ts (Vite 8+, rolldown/oxc)
345
+ export default defineConfig({
346
+ oxc: {
347
+ jsx: { importSource: 'ajo' },
348
+ },
349
+ })
350
+
351
+ // vite.config.ts (Vite 7 and below, esbuild)
344
352
  export default defineConfig({
345
353
  esbuild: {
346
354
  jsx: 'automatic',
@@ -349,4 +357,30 @@ export default defineConfig({
349
357
  })
350
358
  ```
351
359
 
360
+ And in tsconfig, so JSX types resolve in the editor:
361
+
362
+ ```jsonc
363
+ // tsconfig.json
364
+ {
365
+ "compilerOptions": {
366
+ "jsx": "react-jsx",
367
+ "jsxImportSource": "ajo"
368
+ }
369
+ }
370
+ ```
371
+
352
372
  For other build systems: `jsx: 'react-jsx'` (or `'automatic'`), `jsxImportSource: 'ajo'`. No manual imports needed — the build tool auto-imports from `ajo/jsx-runtime`.
373
+
374
+ Mount and server-render:
375
+
376
+ ```tsx
377
+ import { render } from 'ajo'
378
+
379
+ render(<App />, document.getElementById('root')!)
380
+ // render(h, el, child?, ref?) — optional child/ref bound the reconciled
381
+ // range to [child, ref) for hydration or partial updates inside el
382
+
383
+ import { render as toString } from 'ajo/html'
384
+
385
+ const html = toString(<App />) // SSR: stateful components run one iteration, then finalize
386
+ ```
package/dist/html.js CHANGED
@@ -1 +1 @@
1
- import{isVNode as t,mark as n}from"./jsx.js";import{Context as e,current as o}from"./context.js";var{keys:r}=Object,a=new Set(["area","base","br","col","command","embed","hr","img","input","keygen","link","meta","param","source","track","wbr"]),l=Symbol.for("ajo.args"),s=e=>e.replace(/[&<>"']/g,e=>`&#${e.charCodeAt(0)};`),i=/^[A-Za-z][\w:.-]*$/,m=/^[^\s"'/>=\0-\x1F\x7F]+$/,f=()=>{},c={tag:"div"},u=e=>{let t="";return d(e,e=>t+=e),t},d=(e,r)=>{if(null==e)return;let a=typeof e;if("boolean"!=a)if("string"==a)r(s(e));else if("number"==a||"bigint"==a)r(s(String(e)));else if(t(e))"function"==typeof e.nodeName?b(e,r):g(e,r);else if(Symbol.iterator in Object(e))for(e of e)d(e,r);else r(s(String(e)))},g=(e,t)=>{let{nodeName:o,children:n}=e;o="string"==typeof o&&i.test(o)?o:c.tag;let l="";for(let t of r(e))"nodeName"==t||"children"==t||"key"==t||"skip"==t||"memo"==t||"ref"==t||t.startsWith("set:")||!m.test(t)||null==e[t]||!1===e[t]||(!0===e[t]?l+=` ${t}`:l+=` ${t}="${s(String(e[t]))}"`);t(`<${o}${l}>`),a.has(o)||(null!=n&&d(n,t),t(`</${o}>`))},b=({nodeName:e,...t},r)=>{"GeneratorFunction"==e.constructor.name?h(e,t,r):d(e(t),r)},h=(t,a,s)=>{let i={...t.attrs},m={...t.args};for(let e of r(a))e.startsWith("attr:")?i[e.slice(5)]=a[e]:"key"==e||"skip"==e||"memo"==e||"ref"==e||e.startsWith("set:")?i[e]=a[e]:m[e]=a[e];let u=new AbortController,d={*[Symbol.iterator](){for(;;)yield this[l]},[e]:Object.create(o()?.[e]??null),[l]:m,signal:u.signal,next:f,return:f,throw:e=>{throw e}},b=t.call(d,m),h=o();o(d);let y=e=>n({...i,nodeName:t.is??c.tag,children:e}),p="";try{g(y(b.next().value),e=>p+=e)}catch(t){p="",g(y(b.throw(t).value),e=>p+=e)}finally{b.return(),u.abort(),o(h)}s(p)};export{c as defaults,d as html,u as render};
1
+ import{isVNode as t}from"./jsx.js";import{Context as n,current as e}from"./context.js";var{keys:o}=Object,r=new Set(["area","base","br","col","command","embed","hr","img","input","keygen","link","meta","param","source","track","wbr"]),a=Symbol.for("ajo.args"),l=e=>e.replace(/[&<>"']/g,e=>`&#${e.charCodeAt(0)};`),s=/^[A-Za-z][\w:.-]*$/,i=/^[^\s"'/>=\0-\x1F\x7F]+$/,m=()=>{},c={tag:"div"},f=e=>{let t="";return u(e,e=>t+=e),t},u=(e,r)=>{if(null==e)return;let a=typeof e;if("boolean"!=a)if("string"==a)r(l(e));else if("number"==a||"bigint"==a)r(l(String(e)));else if(t(e))"function"==typeof e.nodeName?g(e,r):d(e,r);else if(Symbol.iterator in Object(e))for(e of e)u(e,r);else r(l(String(e)))},d=(e,t)=>{let{nodeName:a,children:n}=e;a="string"==typeof a&&s.test(a)?a:c.tag;let m="";for(let t of o(e))"nodeName"==t||"children"==t||"key"==t||"skip"==t||"memo"==t||"ref"==t||t.startsWith("set:")||!i.test(t)||null==e[t]||!1===e[t]||(!0===e[t]?m+=` ${t}`:m+=` ${t}="${l(String(e[t]))}"`);t(`<${a}${m}>`),r.has(a)||(null!=n&&u(n,t),t(`</${a}>`))},g=({nodeName:e,...t},r)=>{"GeneratorFunction"==e.constructor.name?h(e,t,r):u(e(t),r)},h=(t,r,l)=>{let s={...t.attrs},i={...t.args};for(let e of o(r))e.startsWith("attr:")?s[e.slice(5)]=r[e]:"key"==e||"skip"==e||"memo"==e||"ref"==e||e.startsWith("set:")?s[e]=r[e]:i[e]=r[e];s.nodeName=t.is??c.tag;let f=new AbortController,u={*[Symbol.iterator](){for(;;)yield this[a]},[n]:Object.create(e()?.[n]??null),[a]:i,signal:f.signal,next:m,return:m,throw:e=>{throw e}},g=t.call(u,i),h=e();e(u);let b="",y=e=>b+=e;try{s.children=g.next().value,d(s,y)}catch(t){b="",s.children=g.throw(t).value,d(s,y)}finally{g.return(),f.abort(),e(h)}l(b)};export{c as defaults,u as html,f as render};
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{isVNode as t,mark as n}from"./jsx.js";import{Context as e,current as o}from"./context.js";var y,{isArray:r}=Array,{assign:a,hasOwn:l,keys:s}=Object,i=Symbol.for("ajo.key"),m=Symbol.for("ajo.keyed"),f=Symbol.for("ajo.memo"),c=Symbol.for("ajo.cache"),u=Symbol.for("ajo.generator"),d=Symbol.for("ajo.iterator"),g=Symbol.for("ajo.render"),b=Symbol.for("ajo.args"),h=Symbol.for("ajo.controller"),p={tag:"div"},S=(e,t)=>(t&&(e.is=t),e),x=(e,t,r=t.firstChild,n=null)=>{if(C(e,e=>{let i="string"==typeof e?E(e,r,n):k(e,t,r,n);null==r?A(t,i,n):i==r?r=i.nextSibling:i==r.nextSibling?(A(t,r,n),r=i.nextSibling):A(t,i,r)}),r&&!n&&r==t.firstChild){let e=[];for(t[m]?.clear();r;)e.push(r),r=r.nextSibling;for(r of(t.textContent="",e))1==r.nodeType&&O(r)}else for(;r&&r!=n;){let e=r.nextSibling,n=t.removeChild(r);1==n.nodeType&&O(n,t),r=e}},C=(e,r)=>{if(null==e)return;let n=typeof e;if("boolean"!=n)if("string"==n)r(e);else if("number"==n||"bigint"==n)r(String(e));else if(t(e))"function"==typeof e.nodeName?N(e,r):r(e);else if(Symbol.iterator in Object(e))for(e of e)C(e,r);else r(String(e))},N=({nodeName:e,...t},r)=>{"GeneratorFunction"==e.constructor.name?r(j(e,t)):C(e(t),r)},j=(e,t)=>{let r={...e.attrs},i={...e.args};for(let e of s(t))e.startsWith("attr:")?r[e.slice(5)]=t[e]:"key"==e||"skip"==e||"memo"==e||"ref"==e||e.startsWith("set:")?r[e]=t[e]:i[e]=t[e];return n({...r,nodeName:e.is??p.tag,[u]:e,[b]:i})},E=(e,t,r)=>{for(;t&&t!=r&&3!=t.nodeType;)t=t.nextSibling;return t&&t!=r?t.data!=e&&(t.data=e):t=document.createTextNode(e),t},k=(e,t,r,n)=>{let o=e.nodeName,l=e.key,s=e[u];for(r&&null!=l&&null==n&&(r=(t[m]??=new Map).get(l)??r);r&&r!=n&&(r.localName!=o||null!=r[i]&&r[i]!=l||r[u]&&r[u]!=s);)r=null==r[i]?r.nextElementSibling:null;return r==n&&(r=null),r??=document.createElementNS(e.xmlns??t.namespaceURI,o),null!=l&&(t[m]??=new Map).set(r[i]=l,r),(!r[c]||null==e.memo||w(r[f],e.memo))&&(v(r[c],e,r),e.skip||(s?T(s,e[b],r):r[c]||"string"!=typeof e.children&&"number"!=typeof e.children?x(e.children,r):r.textContent=e.children+""),r[f]=e.memo,r[c]=e),r},v=(e,t,r)=>{if(!e)for(let e=r.attributes.length;e--;)l(t,r.attributes[e].name)||r.removeAttribute(r.attributes[e].name);for(let n in t)if(l(t,n)){let i=t[n];if(e&&e[n]===i||"nodeName"==n||"children"==n||"key"==n||"skip"==n||"memo"==n)continue;"ref"==n&&"function"==typeof i?i(r):n.startsWith("set:")?r[n.slice(4)]=i:null==i||!1===i?r.removeAttribute(n):"class"==n&&"string"==typeof r.className?r.className=!0===i?"":i:r.setAttribute(n,!0===i?"":i)}if(e)for(let n in e)if(l(e,n)){if(l(t,n)||"nodeName"==n||"children"==n||"key"==n||"skip"==n||"memo"==n)continue;n.startsWith("set:")?r[n.slice(4)]=void 0:r.removeAttribute(n)}},w=(e,t)=>{if(r(e)&&r(t)){if(e.length!=t.length)return!0;for(let r=e.length;r--;)if(e[r]!==t[r])return!0;return!1}return e!==t},A=(e,t,r)=>{if(t.isConnected&&t.contains(document.activeElement)){let n=t.nextSibling;for(;r&&r!=t;){let t=r.nextSibling;e.insertBefore(r,n),r=t}}else e.insertBefore(t,r)},O=(e,t)=>{if(e[f]==O)return;e[f]=O;let r=e;for(;r.firstElementChild;)r=r.firstElementChild;for(;;){let{nextElementSibling:n,parentNode:o}=r;if(null!=r[i]&&(o??t)?.[m]?.delete(r[i]),"function"==typeof r.return&&r.return(!1),"function"==typeof r[c]?.ref&&r[c].ref(null),r===e)break;if(r=n??o??e,n)for(;r.firstElementChild;)r=r.firstElementChild}},T=(t,r,n)=>{n[u]??=(W(),a(n,M)[e]=Object.create(o()?.[e]??null),t);for(let e of s(n[b]??=r))l(r,e)||delete n[b][e];a(n[b],r),n[g]()},W=()=>y||(y=new MutationObserver(e=>{for(let t of e)for(let e of t.removedNodes)1==e.nodeType&&!e.isConnected&&O(e,t.target)})).observe(document,{childList:!0,subtree:!0}),M={*[Symbol.iterator](){for(;;)yield this[b]},[g](){let e=o();o(this);try{this[d]||(this.signal=(this[h]=new AbortController).signal,this[d]=this[u].call(this,this[b]));let{value:e,done:t}=this[d].next();x(e,this),t&&this.return()}catch(e){this.throw(e)}finally{o(e)}},next(e,t){if(!1===this[d]||!this.isConnected)return t;try{"function"==typeof e&&(t=e.call(this,this[b]))}catch(e){return this.throw(e)}return o()?.contains(this)||this[g](),t},throw(e){for(let t=this;t;t=t.parentNode)if(t[d]?.throw)try{return x(t[d].throw(e).value,t)}catch(t){e=Error(t?.message??t,{cause:e})}throw e},return(e=!0){if(!1===this[d])return;let t=this[d];this[d]=!1,e&&((e,t)=>{let r=t.firstElementChild;for(;r;)if(e(r)&&r.firstElementChild)r=r.firstElementChild;else{for(;r!=t&&!r.nextElementSibling;)r=r.parentNode??t;r=r!=t&&r.nextElementSibling}})(e=>"function"!=typeof e.return||e.return(),this);try{t?.return()}catch(e){this.throw(e)}finally{this[d]=null,this[h]?.abort()}}};export{p as defaults,x as render,S as stateful};
1
+ import{isVNode as t}from"./jsx.js";import{Context as n,current as e}from"./context.js";var b,{isArray:o}=Array,{assign:r,hasOwn:a,keys:l}=Object,s=Symbol.for("ajo.key"),i=Symbol.for("ajo.keyed"),m=Symbol.for("ajo.memo"),c=Symbol.for("ajo.cache"),f=Symbol.for("ajo.generator"),u=Symbol.for("ajo.iterator"),d=Symbol.for("ajo.render"),g=Symbol.for("ajo.args"),h=Symbol.for("ajo.controller"),y={tag:"div"},p=(e,t)=>(t&&(e.is=t),e),S=(e,t,r=t.firstChild,n=null)=>{if(x(e,e=>{let i="string"==typeof e?E(e,r,n):N(e,t,r,n);null==r?A(t,i,n):i==r?r=i.nextSibling:i==r.nextSibling?(r.contains(document.activeElement)?t.insertBefore(i,r):t.insertBefore(r,n),r=i.nextSibling):A(t,i,r)}),r&&!n&&r==t.firstChild){let e=[...t.children];t[i]?.clear(),t.textContent="";for(let t of e)O(t)}else for(;r&&r!=n;){let e=r.nextSibling;t.removeChild(r),1==r.nodeType&&O(r,t),r=e}},x=(e,r)=>{let n=typeof e;if(null!=e&&"boolean"!=n)if("string"==n)r(e);else if("number"==n||"bigint"==n)r(String(e));else if(t(e))"function"==typeof e.nodeName?C(e,r):r(e);else if(Symbol.iterator in Object(e))for(e of e)x(e,r);else r(String(e))},C=({nodeName:e,...t},r)=>{"GeneratorFunction"==e.constructor.name?r(j(e,t)):x(e(t),r)},j=(e,t)=>{let r={...e.attrs},n={...e.args};for(let e of l(t))e.startsWith("attr:")?r[e.slice(5)]=t[e]:"key"==e||"skip"==e||"memo"==e||"ref"==e||e.startsWith("set:")?r[e]=t[e]:n[e]=t[e];return r.nodeName=e.is??y.tag,r[f]=e,r[g]=n,r},E=(e,t,r)=>{for(;t&&t!=r&&3!=t.nodeType;)t=t.nextSibling;return null==t||t==r?t=document.createTextNode(e):t.data!=e&&(t.data=e),t},N=(e,t,r,n)=>{let l=e.nodeName,o=e.key,a=e[f];for(r&&null!=o&&null==n&&(r=t[i]?.get(o)??r);r&&r!=n;){let e=r[s],t=r[f];if(!(r.localName!=l||null!=e&&e!=o||t&&t!=a))break;r=null==e?r.nextElementSibling:null}return(null==r||r==n)&&(r=document.createElementNS(e.xmlns??t.namespaceURI,l)),null!=o&&(t[i]??=new Map).set(r[s]=o,r),(!r[c]||null==e.memo||w(r[m],e.memo))&&(k(r[c],e,r),e.skip||(a?B(a,e[g],r):r[c]||"string"!=typeof e.children&&"number"!=typeof e.children?S(e.children,r):r.textContent=e.children+""),r[m]=e.memo,r[c]=e),r},v=e=>"nodeName"==e||"children"==e||"key"==e||"skip"==e||"memo"==e,k=(e,t,r)=>{if(!e)for(let e=r.attributes.length;e--;){let{name:n}=r.attributes[e];a(t,n)||r.removeAttribute(n)}for(let n in t)if(a(t,n)){let i=t[n];if(e&&e[n]===i||v(n))continue;"ref"==n&&"function"==typeof i?i(r):n.startsWith("set:")?r[n.slice(4)]=i:null==i||!1===i?r.removeAttribute(n):"class"==n&&"string"==typeof r.className?r.className=!0===i?"":i:r.setAttribute(n,!0===i?"":i)}if(e)for(let n in e)if(a(e,n)){if(a(t,n)||v(n))continue;n.startsWith("set:")?r[n.slice(4)]=void 0:r.removeAttribute(n)}},w=(e,t)=>{if(o(e)&&o(t)){if(e.length!=t.length)return!0;for(let r=e.length;r--;)if(e[r]!==t[r])return!0;return!1}return e!==t},A=(e,t,r)=>{if(t.isConnected&&t.contains(document.activeElement)){let n=t.nextSibling;for(;r&&r!=t;){let t=r.nextSibling;e.insertBefore(r,n),r=t}}else e.insertBefore(t,r)},O=(e,t)=>{if(e[m]==O)return;e[m]=O;let r=e;for(;r.firstElementChild;)r=r.firstElementChild;for(;;){let{nextElementSibling:n,parentNode:l}=r;if(null!=r[s]&&(l??t)?.[i]?.delete(r[s]),"function"==typeof r.return&&r.return(!1),"function"==typeof r[c]?.ref&&r[c].ref(null),r==e)break;if(r=n??l??e,n)for(;r.firstElementChild;)r=r.firstElementChild}},B=(t,i,o)=>{o[f]||(T(),r(o,W)[n]=Object.create(e()?.[n]??null),o[f]=t);let s=o[g]??=i;for(let e of l(s))a(i,e)||delete s[e];r(s,i),o[d]()},T=()=>b||(b=new MutationObserver(e=>{for(let t of e)for(let e of t.removedNodes)1==e.nodeType&&!e.isConnected&&O(e,t.target)})).observe(document,{childList:!0,subtree:!0}),W={*[Symbol.iterator](){for(;;)yield this[g]},[d](){let t=e();e(this);try{this[u]||(this.signal=(this[h]=new AbortController).signal,this[u]=this[f].call(this,this[g]));let{value:e,done:t}=this[u].next();S(e,this),t&&this.return()}catch(t){this.throw(t)}finally{e(t)}},next(t,r){if(!1===this[u]||!this.isConnected)return r;try{"function"==typeof t&&(r=t.call(this,this[g]))}catch(t){return this.throw(t)}return e()?.contains(this)||this[d](),r},throw(e){for(let t=this;t;t=t.parentNode)if(t[u]?.throw)try{return S(t[u].throw(e).value,t)}catch(t){e=Error(t?.message??t,{cause:e})}throw e},return(e=!0){let t=this[u];if(!1!==t){this[u]=!1,e&&((e,t)=>{let r=t.firstElementChild;for(;r;)if(e(r)&&r.firstElementChild)r=r.firstElementChild;else{for(;r!=t&&!r.nextElementSibling;)r=r.parentNode??t;r=r!=t&&r.nextElementSibling}})(e=>"function"!=typeof e.return||e.return(),this);try{t?.return()}catch(e){this.throw(e)}finally{this[u]=null,this[h]?.abort()}}}};export{y as defaults,S as render,p as stateful};
package/dist/jsx.js CHANGED
@@ -1 +1 @@
1
- var t=Symbol.for("ajo.vnode"),n=a=>(a[t]=a,a),e=a=>a?.[t]===a,o=a=>a.children;function r(a,s,e){return(s??={}).nodeName=a,null!=e&&(s.key=e),n(s)}var a=r,l=r;export{o as Fragment,e as isVNode,r as jsx,l as jsxDEV,a as jsxs,n as mark};
1
+ var t=Symbol(),n=a=>(a[t]=a,a),e=a=>a?.[t]===a,o=a=>a.children;function r(a,s,e){return(s??={}).nodeName=a,null!=e&&(s.key=e),n(s)}var a=r,l=r;export{o as Fragment,e as isVNode,r as jsx,l as jsxDEV,a as jsxs,n as mark};
package/html.js CHANGED
@@ -1,17 +1,17 @@
1
- import { Context, current } from './context.js'
2
- import { isVNode, mark } from './jsx.js'
3
-
4
- const { keys } = Object
5
-
6
- const Void = new Set(['area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr'])
7
-
8
- const Args = Symbol.for('ajo.args')
9
-
10
- const escape = s => s.replace(/[&<>"']/g, c => `&#${c.charCodeAt(0)};`)
11
-
12
- const Tag = /^[A-Za-z][\w:.-]*$/, Attr = /^[^\s"'/>=\0-\x1F\x7F]+$/
13
-
14
- const noop = () => { }
1
+ import { Context, current } from './context.js'
2
+ import { isVNode } from './jsx.js'
3
+
4
+ const { keys } = Object
5
+
6
+ const Void = new Set(['area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr'])
7
+
8
+ const Args = Symbol.for('ajo.args')
9
+
10
+ const escape = s => s.replace(/[&<>"']/g, c => `&#${c.charCodeAt(0)};`)
11
+
12
+ const Tag = /^[A-Za-z][\w:.-]*$/, Attr = /^[^\s"'/>=\0-\x1F\x7F]+$/
13
+
14
+ const noop = () => { }
15
15
 
16
16
  export const defaults = { tag: 'div' }
17
17
 
@@ -24,53 +24,53 @@ export const render = h => {
24
24
  return out
25
25
  }
26
26
 
27
- export const html = (h, emit) => {
28
-
29
- if (h == null) return
30
-
31
- const type = typeof h
32
-
33
- if (type == 'boolean') return
27
+ export const html = (h, emit) => {
28
+
29
+ if (h == null) return
30
+
31
+ const type = typeof h
32
+
33
+ if (type == 'boolean') return
34
34
 
35
35
  if (type == 'string') emit(escape(h))
36
36
 
37
- else if (type == 'number' || type == 'bigint') emit(escape(String(h)))
37
+ else if (type == 'number' || type == 'bigint') emit(escape(String(h)))
38
38
 
39
- else if (isVNode(h)) typeof h.nodeName == 'function' ? run(h, emit) : element(h, emit)
40
-
41
- else if (Symbol.iterator in Object(h)) for (h of h) html(h, emit)
42
-
43
- else emit(escape(String(h)))
44
- }
39
+ else if (isVNode(h)) typeof h.nodeName == 'function' ? run(h, emit) : element(h, emit)
40
+
41
+ else if (Symbol.iterator in Object(h)) for (h of h) html(h, emit)
42
+
43
+ else emit(escape(String(h)))
44
+ }
45
45
 
46
46
  const element = (h, emit) => {
47
47
 
48
- let { nodeName, children } = h
49
-
50
- nodeName = typeof nodeName == 'string' && Tag.test(nodeName) ? nodeName : defaults.tag
51
-
52
- let a = ''
53
-
54
- for (const key of keys(h)) {
55
-
56
- if (key == 'nodeName' || key == 'children' || key == 'key' || key == 'skip' || key == 'memo' || key == 'ref' || key.startsWith('set:') || !Attr.test(key) || h[key] == null || h[key] === false) continue
57
-
58
- if (h[key] === true) a += ` ${key}`
59
-
60
- else a += ` ${key}="${escape(String(h[key]))}"`
61
- }
62
-
63
- emit(`<${nodeName}${a}>`)
64
-
65
- if (!Void.has(nodeName)) {
66
-
67
- if (children != null) html(children, emit)
68
-
69
- emit(`</${nodeName}>`)
48
+ let { nodeName, children } = h
49
+
50
+ nodeName = typeof nodeName == 'string' && Tag.test(nodeName) ? nodeName : defaults.tag
51
+
52
+ let a = ''
53
+
54
+ for (const key of keys(h)) {
55
+
56
+ if (key == 'nodeName' || key == 'children' || key == 'key' || key == 'skip' || key == 'memo' || key == 'ref' || key.startsWith('set:') || !Attr.test(key) || h[key] == null || h[key] === false) continue
57
+
58
+ if (h[key] === true) a += ` ${key}`
59
+
60
+ else a += ` ${key}="${escape(String(h[key]))}"`
61
+ }
62
+
63
+ emit(`<${nodeName}${a}>`)
64
+
65
+ if (!Void.has(nodeName)) {
66
+
67
+ if (children != null) html(children, emit)
68
+
69
+ emit(`</${nodeName}>`)
70
70
  }
71
71
  }
72
72
 
73
- const run = ({ nodeName, ...h }, emit) => {
73
+ const run = ({ nodeName, ...h }, emit) => {
74
74
 
75
75
  if (nodeName.constructor.name == 'GeneratorFunction') runGenerator(nodeName, h, emit)
76
76
 
@@ -81,7 +81,7 @@ const runGenerator = (fn, h, emit) => {
81
81
 
82
82
  const attrs = { ...fn.attrs }, args = { ...fn.args }
83
83
 
84
- for (const key of keys(h)) {
84
+ for (const key of keys(h)) {
85
85
 
86
86
  if (key.startsWith('attr:')) attrs[key.slice(5)] = h[key]
87
87
 
@@ -90,11 +90,13 @@ const runGenerator = (fn, h, emit) => {
90
90
  else args[key] = h[key]
91
91
  }
92
92
 
93
+ attrs.nodeName = fn.is ?? defaults.tag
94
+
93
95
  const controller = new AbortController()
94
96
 
95
97
  const instance = {
96
98
 
97
- *[Symbol.iterator]() { while (true) yield this[Args] },
99
+ *[Symbol.iterator]() { while (true) yield this[Args] },
98
100
 
99
101
  [Context]: Object.create(current()?.[Context] ?? null),
100
102
 
@@ -115,19 +117,23 @@ const runGenerator = (fn, h, emit) => {
115
117
 
116
118
  current(instance)
117
119
 
118
- const vnode = children => mark({ ...attrs, nodeName: fn.is ?? defaults.tag, children })
119
-
120
- let out = ''
121
-
122
- try {
123
-
124
- element(vnode(iterator.next().value), chunk => out += chunk)
125
-
126
- } catch (error) {
127
-
128
- out = ''
129
-
130
- element(vnode(iterator.throw(error).value), chunk => out += chunk)
120
+ let out = ''
121
+
122
+ const write = chunk => out += chunk
123
+
124
+ try {
125
+
126
+ attrs.children = iterator.next().value
127
+
128
+ element(attrs, write)
129
+
130
+ } catch (error) {
131
+
132
+ out = ''
133
+
134
+ attrs.children = iterator.throw(error).value
135
+
136
+ element(attrs, write)
131
137
 
132
138
  } finally {
133
139
 
package/index.js CHANGED
@@ -1,375 +1,401 @@
1
- import { Context, current } from './context.js'
2
- import { isVNode, mark } from './jsx.js'
3
-
4
- const { isArray } = Array, { assign, hasOwn, keys } = Object
5
-
6
- const Key = Symbol.for('ajo.key')
7
- const Keyed = Symbol.for('ajo.keyed')
8
- const Memo = Symbol.for('ajo.memo')
9
- const Cache = Symbol.for('ajo.cache')
10
- const Generator = Symbol.for('ajo.generator')
11
- const Iterator = Symbol.for('ajo.iterator')
12
- const Render = Symbol.for('ajo.render')
13
- const Args = Symbol.for('ajo.args')
14
- const Controller = Symbol.for('ajo.controller')
15
-
16
- export const defaults = { tag: 'div' }
17
-
18
- export const stateful = (fn, is) => (is && (fn.is = is), fn)
19
-
20
- export const render = (h, el, child = el.firstChild, ref = null) => {
21
-
22
- walk(h, h => {
23
-
24
- const node = typeof h == 'string' ? text(h, child, ref) : element(h, el, child, ref)
25
-
26
- if (child == null) {
27
-
28
- before(el, node, ref)
29
-
30
- } else if (node == child) {
31
-
32
- child = node.nextSibling
33
-
34
- } else if (node == child.nextSibling) {
35
-
36
- before(el, child, ref)
37
-
38
- child = node.nextSibling
39
-
40
- } else {
41
-
42
- before(el, node, child)
43
- }
44
- })
45
-
46
- if (child && !ref && child == el.firstChild) {
47
-
48
- const gone = []
49
-
50
- el[Keyed]?.clear()
51
-
52
- while (child) gone.push(child), child = child.nextSibling
53
-
54
- el.textContent = ''
55
-
56
- for (child of gone) if (child.nodeType == 1) unref(child)
57
-
58
- } else while (child && child != ref) {
59
-
60
- const node = child.nextSibling
61
-
62
- const gone = el.removeChild(child)
63
-
64
- if (gone.nodeType == 1) unref(gone, el)
65
-
66
- child = node
67
- }
68
- }
69
-
70
- const walk = (h, fn) => {
71
-
72
- if (h == null) return
73
-
74
- const type = typeof h
75
-
76
- if (type == 'boolean') return
77
-
78
- if (type == 'string') fn(h)
79
-
80
- else if (type == 'number' || type == 'bigint') fn(String(h))
81
-
82
- else if (isVNode(h)) typeof h.nodeName == 'function' ? run(h, fn) : fn(h)
83
-
84
- else if (Symbol.iterator in Object(h)) for (h of h) walk(h, fn)
85
-
86
- else fn(String(h))
87
- }
88
-
89
- const run = ({ nodeName, ...h }, fn) => {
90
-
91
- if (nodeName.constructor.name == 'GeneratorFunction') fn(runGenerator(nodeName, h))
92
-
93
- else walk(nodeName(h), fn)
94
- }
95
-
96
- const runGenerator = (fn, h) => {
97
-
98
- const attrs = { ...fn.attrs }, args = { ...fn.args }
99
-
100
- for (const key of keys(h)) {
101
-
102
- if (key.startsWith('attr:')) attrs[key.slice(5)] = h[key]
103
-
104
- else if (key == 'key' || key == 'skip' || key == 'memo' || key == 'ref' || key.startsWith('set:')) attrs[key] = h[key]
105
-
106
- else args[key] = h[key]
107
- }
108
-
109
- return mark({ ...attrs, nodeName: fn.is ?? defaults.tag, [Generator]: fn, [Args]: args })
110
- }
111
-
112
- const text = (h, node, ref) => {
113
-
114
- while (node && node != ref && node.nodeType != 3) node = node.nextSibling
115
-
116
- node && node != ref ? node.data != h && (node.data = h) : node = document.createTextNode(h)
117
-
118
- return node
119
- }
120
-
121
- const element = (h, el, node, ref) => {
122
-
123
- const nodeName = h.nodeName, key = h.key, gen = h[Generator]
124
-
125
- if (node && key != null && ref == null) node = (el[Keyed] ??= new Map()).get(key) ?? node
126
-
127
- while (node && node != ref && (
128
-
129
- (node.localName != nodeName) ||
130
-
131
- (node[Key] != null && node[Key] != key) ||
132
-
133
- (node[Generator] && node[Generator] != gen)
134
-
135
- )) node = node[Key] != null ? null : node.nextElementSibling
136
-
137
- if (node == ref) node = null
138
-
139
- node ??= document.createElementNS(h.xmlns ?? el.namespaceURI, nodeName)
140
-
141
- if (key != null) (el[Keyed] ??= new Map()).set(node[Key] = key, node)
142
-
143
- if (!node[Cache] || h.memo == null || some(node[Memo], h.memo)) {
144
-
145
- attrs(node[Cache], h, node)
146
-
147
- if (!h.skip) gen ? next(gen, h[Args], node) : !node[Cache] && (typeof h.children == 'string' || typeof h.children == 'number') ? node.textContent = h.children + '' : render(h.children, node)
148
-
149
- node[Memo] = h.memo
150
-
151
- node[Cache] = h
152
- }
153
-
154
- return node
155
- }
156
-
157
- const attrs = (cache, h, node) => {
158
-
159
- if (!cache) for (let i = node.attributes.length; i--;) hasOwn(h, node.attributes[i].name) || node.removeAttribute(node.attributes[i].name)
160
-
161
- for (const key in h) if (hasOwn(h, key)) {
162
-
163
- const value = h[key]
164
-
165
- if (cache && cache[key] === value || key == 'nodeName' || key == 'children' || key == 'key' || key == 'skip' || key == 'memo') continue
166
-
167
- if (key == 'ref' && typeof value == 'function') value(node)
168
-
169
- else if (key.startsWith('set:')) node[key.slice(4)] = value
170
-
171
- else if (value == null || value === false) node.removeAttribute(key)
172
-
173
- else if (key == 'class' && typeof node.className == 'string') node.className = value === true ? '' : value
174
-
175
- else node.setAttribute(key, value === true ? '' : value)
176
- }
177
-
178
- if (cache) for (const key in cache) if (hasOwn(cache, key)) {
179
-
180
- if (hasOwn(h, key) || key == 'nodeName' || key == 'children' || key == 'key' || key == 'skip' || key == 'memo') continue
181
-
182
- if (key.startsWith('set:')) node[key.slice(4)] = undefined
183
-
184
- else node.removeAttribute(key)
185
- }
186
- }
187
-
188
- const some = (a, b) => {
189
-
190
- if (isArray(a) && isArray(b)) {
191
-
192
- if (a.length != b.length) return true
193
-
194
- for (let i = a.length; i--;) if (a[i] !== b[i]) return true
195
-
196
- return false
197
- }
198
-
199
- return a !== b
200
- }
201
-
202
- const each = (fn, node) => {
203
-
204
- let child = node.firstElementChild
205
-
206
- while (child)
207
-
208
- if (fn(child) && child.firstElementChild) child = child.firstElementChild
209
-
210
- else {
211
-
212
- while (child != node && !child.nextElementSibling) child = child.parentNode ?? node
213
-
214
- child = child != node && child.nextElementSibling
215
- }
216
- }
217
-
218
- const before = (el, node, child) => {
219
-
220
- if (node.isConnected && node.contains(document.activeElement)) {
221
-
222
- const ref = node.nextSibling
223
-
224
- while (child && child != node) {
225
-
226
- const next = child.nextSibling
227
-
228
- el.insertBefore(child, ref)
229
-
230
- child = next
231
- }
232
-
233
- } else el.insertBefore(node, child)
234
- }
235
-
236
- const unref = (root, parent) => {
237
-
238
- if (root[Memo] == unref) return
239
-
240
- root[Memo] = unref
241
-
242
- let node = root
243
-
244
- while (node.firstElementChild) node = node.firstElementChild
245
-
246
- while (true) {
247
-
248
- const { nextElementSibling, parentNode } = node
249
-
250
- if (node[Key] != null) (parentNode ?? parent)?.[Keyed]?.delete(node[Key])
251
-
252
- if (typeof node.return == 'function') node.return(false)
253
-
254
- if (typeof node[Cache]?.ref == 'function') node[Cache].ref(null)
255
-
256
- if (node === root) break
257
-
258
- node = nextElementSibling ?? parentNode ?? root
259
-
260
- if (nextElementSibling) while (node.firstElementChild) node = node.firstElementChild
261
- }
262
- }
263
-
264
- const next = (fn, args, el) => {
265
-
266
- el[Generator] ??= (watch(), assign(el, methods)[Context] = Object.create(current()?.[Context] ?? null), fn)
267
-
268
- for (const key of keys(el[Args] ??= args)) hasOwn(args, key) || delete el[Args][key]
269
-
270
- assign(el[Args], args)
271
-
272
- el[Render]()
273
- }
274
-
275
- let observer
276
-
277
- const watch = () => observer || (observer = new MutationObserver(records => {
278
-
279
- for (const record of records) for (const node of record.removedNodes) if (node.nodeType == 1 && !node.isConnected) unref(node, record.target)
280
-
281
- })).observe(document, { childList: true, subtree: true })
282
-
283
- const methods = {
284
-
285
- *[Symbol.iterator]() { while (true) yield this[Args] },
286
-
287
- [Render]() {
288
-
289
- const parent = current()
290
-
291
- current(this)
292
-
293
- try {
294
-
295
- if (!this[Iterator]) {
296
-
297
- this.signal = (this[Controller] = new AbortController()).signal
298
-
299
- this[Iterator] = this[Generator].call(this, this[Args])
300
- }
301
-
302
- const { value, done } = this[Iterator].next()
303
-
304
- render(value, this)
305
-
306
- if (done) this.return()
307
-
308
- } catch (e) {
309
-
310
- this.throw(e)
311
-
312
- } finally {
313
-
314
- current(parent)
315
- }
316
- },
317
-
318
- next(fn, result) {
319
-
320
- if (this[Iterator] === false || !this.isConnected) return result
321
-
322
- try {
323
-
324
- if (typeof fn == 'function') result = fn.call(this, this[Args])
325
-
326
- } catch (e) {
327
-
328
- return this.throw(e)
329
- }
330
-
331
- if (!current()?.contains(this)) this[Render]()
332
-
333
- return result
334
- },
335
-
336
- throw(value) {
337
-
338
- for (let el = this; el; el = el.parentNode) if (el[Iterator]?.throw) try {
339
-
340
- return render(el[Iterator].throw(value).value, el)
341
-
342
- } catch (e) {
343
-
344
- value = new Error(e?.message ?? e, { cause: value })
345
- }
346
-
347
- throw value
348
- },
349
-
350
- return(deep = true) {
351
-
352
- if (this[Iterator] === false) return
353
-
354
- const iterator = this[Iterator]
355
-
356
- this[Iterator] = false
357
-
358
- if (deep) each(el => typeof el.return == 'function' ? el.return() : true, this)
359
-
360
- try {
361
-
362
- iterator?.return()
363
-
364
- } catch (e) {
365
-
366
- this.throw(e)
367
-
368
- } finally {
369
-
370
- this[Iterator] = null
371
-
372
- this[Controller]?.abort()
373
- }
374
- }
375
- }
1
+ import { Context, current } from './context.js'
2
+ import { isVNode } from './jsx.js'
3
+
4
+ const { isArray } = Array, { assign, hasOwn, keys } = Object
5
+
6
+ const Key = Symbol.for('ajo.key')
7
+ const Keyed = Symbol.for('ajo.keyed')
8
+ const Memo = Symbol.for('ajo.memo')
9
+ const Cache = Symbol.for('ajo.cache')
10
+ const Generator = Symbol.for('ajo.generator')
11
+ const Iterator = Symbol.for('ajo.iterator')
12
+ const Render = Symbol.for('ajo.render')
13
+ const Args = Symbol.for('ajo.args')
14
+ const Controller = Symbol.for('ajo.controller')
15
+
16
+ export const defaults = { tag: 'div' }
17
+
18
+ export const stateful = (fn, is) => (is && (fn.is = is), fn)
19
+
20
+ export const render = (h, el, child = el.firstChild, ref = null) => {
21
+
22
+ walk(h, h => {
23
+
24
+ const node = typeof h == 'string' ? text(h, child, ref) : element(h, el, child, ref)
25
+
26
+ if (child == null) {
27
+
28
+ before(el, node, ref)
29
+
30
+ } else if (node == child) {
31
+
32
+ child = node.nextSibling
33
+
34
+ } else if (node == child.nextSibling) {
35
+
36
+ if (child.contains(document.activeElement)) el.insertBefore(node, child)
37
+
38
+ else el.insertBefore(child, ref)
39
+
40
+ child = node.nextSibling
41
+
42
+ } else {
43
+
44
+ before(el, node, child)
45
+ }
46
+ })
47
+
48
+ if (child && !ref && child == el.firstChild) {
49
+
50
+ const gone = [...el.children]
51
+
52
+ el[Keyed]?.clear()
53
+
54
+ el.textContent = ''
55
+
56
+ for (const node of gone) unref(node)
57
+
58
+ } else while (child && child != ref) {
59
+
60
+ const node = child.nextSibling
61
+
62
+ el.removeChild(child)
63
+
64
+ if (child.nodeType == 1) unref(child, el)
65
+
66
+ child = node
67
+ }
68
+ }
69
+
70
+ const walk = (h, fn) => {
71
+
72
+ const type = typeof h
73
+
74
+ if (h == null || type == 'boolean') return
75
+
76
+ if (type == 'string') fn(h)
77
+
78
+ else if (type == 'number' || type == 'bigint') fn(String(h))
79
+
80
+ else if (isVNode(h)) typeof h.nodeName == 'function' ? run(h, fn) : fn(h)
81
+
82
+ else if (Symbol.iterator in Object(h)) for (h of h) walk(h, fn)
83
+
84
+ else fn(String(h))
85
+ }
86
+
87
+ const run = ({ nodeName, ...h }, fn) => {
88
+
89
+ if (nodeName.constructor.name == 'GeneratorFunction') fn(runGenerator(nodeName, h))
90
+
91
+ else walk(nodeName(h), fn)
92
+ }
93
+
94
+ const runGenerator = (fn, h) => {
95
+
96
+ const attrs = { ...fn.attrs }, args = { ...fn.args }
97
+
98
+ for (const key of keys(h)) {
99
+
100
+ if (key.startsWith('attr:')) attrs[key.slice(5)] = h[key]
101
+
102
+ else if (key == 'key' || key == 'skip' || key == 'memo' || key == 'ref' || key.startsWith('set:')) attrs[key] = h[key]
103
+
104
+ else args[key] = h[key]
105
+ }
106
+
107
+ attrs.nodeName = fn.is ?? defaults.tag
108
+
109
+ attrs[Generator] = fn
110
+
111
+ attrs[Args] = args
112
+
113
+ return attrs
114
+ }
115
+
116
+ const text = (h, node, ref) => {
117
+
118
+ while (node && node != ref && node.nodeType != 3) node = node.nextSibling
119
+
120
+ if (node == null || node == ref) node = document.createTextNode(h)
121
+
122
+ else if (node.data != h) node.data = h
123
+
124
+ return node
125
+ }
126
+
127
+ const element = (h, el, node, ref) => {
128
+
129
+ const nodeName = h.nodeName, key = h.key, gen = h[Generator]
130
+
131
+ if (node && key != null && ref == null) node = el[Keyed]?.get(key) ?? node
132
+
133
+ while (node && node != ref) {
134
+
135
+ const k = node[Key], g = node[Generator]
136
+
137
+ if (node.localName == nodeName && (k == null || k == key) && (!g || g == gen)) break
138
+
139
+ node = k != null ? null : node.nextElementSibling
140
+ }
141
+
142
+ if (node == null || node == ref) node = document.createElementNS(h.xmlns ?? el.namespaceURI, nodeName)
143
+
144
+ if (key != null) (el[Keyed] ??= new Map()).set(node[Key] = key, node)
145
+
146
+ if (!node[Cache] || h.memo == null || some(node[Memo], h.memo)) {
147
+
148
+ attrs(node[Cache], h, node)
149
+
150
+ if (!h.skip) {
151
+
152
+ if (gen) next(gen, h[Args], node)
153
+
154
+ else if (!node[Cache] && (typeof h.children == 'string' || typeof h.children == 'number')) node.textContent = h.children + ''
155
+
156
+ else render(h.children, node)
157
+ }
158
+
159
+ node[Memo] = h.memo
160
+
161
+ node[Cache] = h
162
+ }
163
+
164
+ return node
165
+ }
166
+
167
+ const special = key => key == 'nodeName' || key == 'children' || key == 'key' || key == 'skip' || key == 'memo'
168
+
169
+ const attrs = (cache, h, node) => {
170
+
171
+ if (!cache) for (let i = node.attributes.length; i--;) {
172
+
173
+ const { name } = node.attributes[i]
174
+
175
+ if (!hasOwn(h, name)) node.removeAttribute(name)
176
+ }
177
+
178
+ for (const key in h) if (hasOwn(h, key)) {
179
+
180
+ const value = h[key]
181
+
182
+ if (cache && cache[key] === value || special(key)) continue
183
+
184
+ if (key == 'ref' && typeof value == 'function') value(node)
185
+
186
+ else if (key.startsWith('set:')) node[key.slice(4)] = value
187
+
188
+ else if (value == null || value === false) node.removeAttribute(key)
189
+
190
+ else if (key == 'class' && typeof node.className == 'string') node.className = value === true ? '' : value
191
+
192
+ else node.setAttribute(key, value === true ? '' : value)
193
+ }
194
+
195
+ if (cache) for (const key in cache) if (hasOwn(cache, key)) {
196
+
197
+ if (hasOwn(h, key) || special(key)) continue
198
+
199
+ if (key.startsWith('set:')) node[key.slice(4)] = undefined
200
+
201
+ else node.removeAttribute(key)
202
+ }
203
+ }
204
+
205
+ const some = (a, b) => {
206
+
207
+ if (isArray(a) && isArray(b)) {
208
+
209
+ if (a.length != b.length) return true
210
+
211
+ for (let i = a.length; i--;) if (a[i] !== b[i]) return true
212
+
213
+ return false
214
+ }
215
+
216
+ return a !== b
217
+ }
218
+
219
+ const each = (fn, node) => {
220
+
221
+ let child = node.firstElementChild
222
+
223
+ while (child)
224
+
225
+ if (fn(child) && child.firstElementChild) child = child.firstElementChild
226
+
227
+ else {
228
+
229
+ while (child != node && !child.nextElementSibling) child = child.parentNode ?? node
230
+
231
+ child = child != node && child.nextElementSibling
232
+ }
233
+ }
234
+
235
+ const before = (el, node, child) => {
236
+
237
+ if (node.isConnected && node.contains(document.activeElement)) {
238
+
239
+ const ref = node.nextSibling
240
+
241
+ while (child && child != node) {
242
+
243
+ const next = child.nextSibling
244
+
245
+ el.insertBefore(child, ref)
246
+
247
+ child = next
248
+ }
249
+
250
+ } else el.insertBefore(node, child)
251
+ }
252
+
253
+ const unref = (root, parent) => {
254
+
255
+ if (root[Memo] == unref) return
256
+
257
+ root[Memo] = unref
258
+
259
+ let node = root
260
+
261
+ while (node.firstElementChild) node = node.firstElementChild
262
+
263
+ while (true) {
264
+
265
+ const { nextElementSibling, parentNode } = node
266
+
267
+ if (node[Key] != null) (parentNode ?? parent)?.[Keyed]?.delete(node[Key])
268
+
269
+ if (typeof node.return == 'function') node.return(false)
270
+
271
+ if (typeof node[Cache]?.ref == 'function') node[Cache].ref(null)
272
+
273
+ if (node == root) break
274
+
275
+ node = nextElementSibling ?? parentNode ?? root
276
+
277
+ if (nextElementSibling) while (node.firstElementChild) node = node.firstElementChild
278
+ }
279
+ }
280
+
281
+ const next = (fn, args, el) => {
282
+
283
+ if (!el[Generator]) {
284
+
285
+ watch()
286
+
287
+ assign(el, methods)[Context] = Object.create(current()?.[Context] ?? null)
288
+
289
+ el[Generator] = fn
290
+ }
291
+
292
+ const prev = el[Args] ??= args
293
+
294
+ for (const key of keys(prev)) if (!hasOwn(args, key)) delete prev[key]
295
+
296
+ assign(prev, args)
297
+
298
+ el[Render]()
299
+ }
300
+
301
+ let observer
302
+
303
+ const watch = () => observer || (observer = new MutationObserver(records => {
304
+
305
+ for (const record of records) for (const node of record.removedNodes) if (node.nodeType == 1 && !node.isConnected) unref(node, record.target)
306
+
307
+ })).observe(document, { childList: true, subtree: true })
308
+
309
+ const methods = {
310
+
311
+ *[Symbol.iterator]() { while (true) yield this[Args] },
312
+
313
+ [Render]() {
314
+
315
+ const parent = current()
316
+
317
+ current(this)
318
+
319
+ try {
320
+
321
+ if (!this[Iterator]) {
322
+
323
+ this.signal = (this[Controller] = new AbortController()).signal
324
+
325
+ this[Iterator] = this[Generator].call(this, this[Args])
326
+ }
327
+
328
+ const { value, done } = this[Iterator].next()
329
+
330
+ render(value, this)
331
+
332
+ if (done) this.return()
333
+
334
+ } catch (e) {
335
+
336
+ this.throw(e)
337
+
338
+ } finally {
339
+
340
+ current(parent)
341
+ }
342
+ },
343
+
344
+ next(fn, result) {
345
+
346
+ if (this[Iterator] === false || !this.isConnected) return result
347
+
348
+ try {
349
+
350
+ if (typeof fn == 'function') result = fn.call(this, this[Args])
351
+
352
+ } catch (e) {
353
+
354
+ return this.throw(e)
355
+ }
356
+
357
+ if (!current()?.contains(this)) this[Render]()
358
+
359
+ return result
360
+ },
361
+
362
+ throw(value) {
363
+
364
+ for (let el = this; el; el = el.parentNode) if (el[Iterator]?.throw) try {
365
+
366
+ return render(el[Iterator].throw(value).value, el)
367
+
368
+ } catch (e) {
369
+
370
+ value = new Error(e?.message ?? e, { cause: value })
371
+ }
372
+
373
+ throw value
374
+ },
375
+
376
+ return(deep = true) {
377
+
378
+ const iterator = this[Iterator]
379
+
380
+ if (iterator === false) return
381
+
382
+ this[Iterator] = false
383
+
384
+ if (deep) each(el => typeof el.return == 'function' ? el.return() : true, this)
385
+
386
+ try {
387
+
388
+ iterator?.return()
389
+
390
+ } catch (e) {
391
+
392
+ this.throw(e)
393
+
394
+ } finally {
395
+
396
+ this[Iterator] = null
397
+
398
+ this[Controller]?.abort()
399
+ }
400
+ }
401
+ }
package/jsx.js CHANGED
@@ -1,4 +1,4 @@
1
- const VNode = Symbol.for('ajo.vnode')
1
+ const VNode = Symbol()
2
2
 
3
3
  export const mark = v => (v[VNode] = v, v)
4
4
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ajo",
3
- "version": "0.1.33",
3
+ "version": "0.1.34",
4
4
  "description": "ajo is a JavaScript view library for building user interfaces",
5
5
  "type": "module",
6
6
  "types": "./types.ts",
package/types.ts CHANGED
@@ -2,7 +2,7 @@ declare module 'ajo' {
2
2
 
3
3
  type Tag = keyof (HTMLElementTagNameMap & SVGElementTagNameMap)
4
4
 
5
- type Type = Tag | (string & {}) | Stateless | Stateful
5
+ type Type = Tag | (string & {}) | Stateless<any> | Stateful<any, any>
6
6
 
7
7
  type Children = unknown
8
8
 
@@ -35,6 +35,12 @@ declare module 'ajo' {
35
35
  [key: `attr:${string}`]: unknown
36
36
  }
37
37
 
38
+ type CommonAttrs = {
39
+ class: string | boolean | null,
40
+ style: string | boolean | null,
41
+ xmlns: string,
42
+ }
43
+
38
44
  interface Defaults { }
39
45
 
40
46
  type DefaultTag = Defaults extends { tag: infer T extends string } ? T : 'div'
@@ -46,7 +52,7 @@ declare module 'ajo' {
46
52
  type Stateful<TArgs extends Args = {}, TTag extends string = DefaultTag> = {
47
53
  (this: StatefulElement<TArgs, TTag>, args: TArgs): Iterator<Children>
48
54
  } & (TTag extends DefaultTag ? { is?: TTag } : { is: TTag }) & {
49
- attrs?: Partial<PropSetter<TTag>> & Args,
55
+ attrs?: Partial<PropSetter<TTag> & CommonAttrs> & Args,
50
56
  args?: Partial<TArgs>,
51
57
  }
52
58
 
@@ -60,13 +66,16 @@ declare module 'ajo' {
60
66
  type StatefulElement<TArgs, TTag> = ElementType<TTag> & {
61
67
  [Symbol.iterator](): Iterator<TArgs>,
62
68
  signal: AbortSignal,
63
- next: <R>(fn?: (this: StatefulElement<TArgs, TTag>, args: TArgs) => R) => R,
69
+ next: {
70
+ (): undefined,
71
+ <R>(fn: (this: StatefulElement<TArgs, TTag>, args: TArgs) => R): R,
72
+ },
64
73
  throw: (value?: unknown) => void,
65
74
  return: (deep?: boolean) => void,
66
75
  }
67
76
 
68
77
  type HTMLIntrinsicElements = {
69
- [TTag in Tag]: Partial<PropSetter<TTag> & SpecialAttrs<ElementType<TTag>>> & Args
78
+ [TTag in Tag]: Partial<PropSetter<TTag> & SpecialAttrs<ElementType<TTag>> & CommonAttrs> & Args
70
79
  }
71
80
 
72
81
  interface IntrinsicElements extends HTMLIntrinsicElements { }
@@ -82,7 +91,7 @@ declare module 'ajo' {
82
91
  : P
83
92
  : P
84
93
 
85
- function render(h: Children, el: ParentNode, child?: ChildNode, ref?: ChildNode): void
94
+ function render(h: Children, el: ParentNode, child?: ChildNode | null, ref?: ChildNode | null): void
86
95
 
87
96
  function stateful<TArgs extends Args, TTag extends string = DefaultTag>(
88
97
  fn: (this: StatefulElement<TArgs, TTag>, args: TArgs) => Iterator<Children>,
@@ -105,11 +114,11 @@ declare module 'ajo/html' {
105
114
  }
106
115
 
107
116
  declare module 'ajo/jsx-runtime' {
108
- import { Type, Args, VNode, ElementChildrenAttribute, IntrinsicElements as _IE } from 'ajo'
109
- export function Fragment({ children }: ElementChildrenAttribute): typeof children
110
- export function jsx(type: Type, props: Args | null, key?: string | number): VNode
111
- export function jsxs(type: Type, props: Args | null, key?: string | number): VNode
112
- export function jsxDEV(type: Type, props: Args | null, key?: string | number): VNode
117
+ import { Type, Args, VNode, ElementChildrenAttribute, IntrinsicElements as _IE } from 'ajo'
118
+ export function Fragment({ children }: ElementChildrenAttribute): typeof children
119
+ export function jsx(type: Type, props: Args | null, key?: string | number): VNode
120
+ export function jsxs(type: Type, props: Args | null, key?: string | number): VNode
121
+ export function jsxDEV(type: Type, props: Args | null, key?: string | number): VNode
113
122
  export namespace JSX {
114
123
  type ElementChildrenAttribute = import('ajo').ElementChildrenAttribute
115
124
  type LibraryManagedAttributes<C, P> = import('ajo').ManagedAttributes<C, P>
@@ -117,9 +126,9 @@ declare module 'ajo/jsx-runtime' {
117
126
  }
118
127
  }
119
128
 
120
- declare module 'ajo/jsx-dev-runtime' {
121
- import { IntrinsicElements as _IE } from 'ajo'
122
- export { Fragment, jsx, jsxs, jsxDEV } from 'ajo/jsx-runtime'
129
+ declare module 'ajo/jsx-dev-runtime' {
130
+ import { IntrinsicElements as _IE } from 'ajo'
131
+ export { Fragment, jsx, jsxs, jsxDEV } from 'ajo/jsx-runtime'
123
132
  export namespace JSX {
124
133
  type ElementChildrenAttribute = import('ajo').ElementChildrenAttribute
125
134
  type LibraryManagedAttributes<C, P> = import('ajo').ManagedAttributes<C, P>