├── .gitignore ├── .npmignore ├── .npmrc ├── LICENSE ├── README.md ├── async.js ├── cjs ├── async.js ├── index.js ├── package.json └── x.js ├── es.js ├── esm.js ├── esm ├── async.js ├── index.js └── x.js ├── index.d.ts ├── index.js ├── min.js ├── min.js.br ├── package.json ├── rollup ├── async.config.js ├── es.config.js ├── esm.config.js └── index.config.js ├── test ├── async.html ├── button.html ├── children.html ├── click.html ├── collapsible.html ├── counter.mjs ├── effect.html ├── effect2.html ├── index.js ├── indirect.html ├── indirect.mjs ├── issue-10.html ├── issue-37.html ├── lfx.html ├── mixed.html ├── multi-return.html ├── package.json ├── registry.mjs ├── state-counter.html ├── swr.html ├── timer.html └── use-mo.html └── uland-head.jpg /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .nyc_output 3 | node_modules/ 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .nyc_output 3 | .travis.yml 4 | node_modules/ 5 | rollup/ 6 | test/ 7 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2020, Andrea Giammarchi, @WebReflection 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 10 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 12 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14 | OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15 | PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🦄 µland 2 | 3 | [![CSP strict](https://webreflection.github.io/csp/strict.svg)](https://webreflection.github.io/csp/#-csp-strict) 4 | 5 | ![tiny island](./uland-head.jpg) 6 | 7 | **Social Media Photo by [Ben Klea](https://unsplash.com/@benkleaphoto) on [Unsplash](https://unsplash.com/)** 8 | 9 | *micro* land, or *unicorn* land, is a [µhtml](https://github.com/WebReflection/uhtml#readme) take at [neverland](https://github.com/WebReflection/neverland/#readme). 10 | 11 | 12 | ### 📣 Community Announcement 13 | 14 | Please ask questions in the [dedicated discussions repository](https://github.com/WebReflection/discussions), to help the community around this project grow ♥ 15 | 16 | --- 17 | 18 | Same API, except the exports are `{Component, render, html, svg}`, where `Component` is a function you can use either as `new Component(...)` or just `Component(...)` which is the equivalent of _neverland_ default export. 19 | 20 | This module exports same utilities via `uland/async`, which is based on `uhooks-dom/async` for asynchronous hooks. 21 | 22 | 23 | ### Announcement 24 | 25 | Are you looking for something even more similar to *React*? Then don't miss [🐪 kaboobie](https://github.com/WebReflection/kaboobie/#readme) out! 26 | 27 | Are you looking for *SSR* components? Then check [🦄 µland-ssr](https://github.com/WebReflection/uland-ssr#readme) out! 28 | 29 | 30 | ## API 31 | 32 | The concept is exactly the same as the _neverland_ one, the `render(...)` accepts a node to render, and either a *component* or a *callback* that returns some content. 33 | 34 | [Live demo](https://codepen.io/WebReflection/pen/dyGvNdg?editors=0010). 35 | 36 | ```js 37 | import {Component, render, html, useState} from 'uland'; 38 | 39 | const Counter = Component((initialState) => { 40 | const [count, setCount] = useState(initialState); 41 | return html` 42 | `; 45 | }); 46 | 47 | // basic example, show two independent counters 48 | render(document.body, () => html` 49 |
50 | A bounce of counters.
51 | ${Counter(0)} ${Counter(1)} 52 |
53 | `); 54 | ``` 55 | 56 | Please [check neverland](https://github.com/WebReflection/neverland/#concept) to know more about this module usage. 57 | -------------------------------------------------------------------------------- /async.js: -------------------------------------------------------------------------------- 1 | self.uland=function(e){"use strict";function t(e){this.observe(e,{subtree:!0,childList:!0})}function n(e){e.type in this&&this[e.type](e)}let s=null,r=new Set;const a=e=>{const{$:t,r:n,h:s}=e;h(n)&&(l.get(s).delete(e),n()),h(e.r=t())&&l.get(s).add(e)},o=()=>{const e=r;r=new Set,e.forEach((({h:e,c:t,a:n,e:s})=>{s&&e.apply(t,n)}))},l=new WeakMap,c=[],i=[];function u(e,t){return e!==this[t]}const d=()=>s,h=e=>"function"==typeof e,p=e=>{const t={h:n,c:null,a:null,e:0,i:0,s:[]};return n;function n(){const n=s;s=t,t.e=t.i=0;try{return e.apply(t.c=this,t.a=arguments)}finally{s=n,c.length&&f.then(c.forEach.bind(c.splice(0),a)),i.length&&i.splice(0).forEach(a)}}},f=Promise.resolve();function w(e){const{_:t,value:n}=this;n!==e&&(this._=new Set,this.value=e,t.forEach((({h:e,c:t,a:n})=>{e.apply(t,n)})))}const y=(e,t)=>{const n=d(),{i:s,s:r}=n;return s!==r.length&&t&&!t.some(u,r[s]._)||(r[s]={$:e(),_:t}),r[n.i++].$},g=e=>(t,n)=>{const s=d(),{i:r,s:a,h:o}=s,c=r===a.length;s.i++,c&&(l.has(o)||l.set(o,new Set),a[r]={$:t,_:n,r:null,d:!1,h:o}),(c||!n||a[r].d||n.some(u,a[r]._))&&e.push(a[r]),a[r].$=t,a[r]._=n,a[r].d=!1},m=g(c),v=g(i),b=(e,t)=>h(t)?t(e):t,E=(e,t,n)=>{const s=d(),{i:a,s:l}=s;a===l.length&&l.push({$:h(n)?n(t):b(void 0,t),set:t=>{l[a].$=e(l[a].$,t),(e=>{r.has(e)||(e.e=1,r.add(e),f.then(o))})(s)}});const{$:c,set:i}=l[s.i++];return[c,i]},k=new WeakMap,x=e=>(e=>{const t=l.get(e);t&&f.then((()=>{t.forEach((e=>{e.r(),e.r=null,e.d=!0})),t.clear()}))})(k.get(e)),C=e=>(e=>l.has(e))(k.get(e)),N=e=>{const t=p(e);return k.set(n,t),n;async function n(){return await t.apply(this,arguments)}}; 2 | /*! (c) Andrea Giammarchi - ISC */ 3 | let $=null,M=null,A=null;const S=new WeakMap,T=new WeakMap,_=(e,t,n,s)=>{const r=r=>{S.has(e)||(S.set(e,0),f.then((()=>{S.delete(e),e.apply(t,n)}))),s(r)};return T.set(s,r),r},L=(e,t,n,s)=>e?[s[0],T.get(s[1])||_(e,t,n,s[1])]:s,O=(e,t)=>{const n=N(t?async function(){const[t,s,r]=[$,M,A];[$,M,A]=[n,this,arguments];try{return await e.apply(M,A)}finally{[$,M,A]=[t,s,r]}}:e);return n},W=((e,s,r,a)=>{const o=new WeakMap,l=new WeakMap,c=new WeakMap,i=e=>o.has(e),u=e=>{i(e)&&(d(e,e.removeEventListener,o.get(e)),o.delete(e))},d=(e,t,n)=>{t.call(e,"disconnected",n),t.call(e,"connected",n)},h=(e,t,n,s)=>{for(let{length:r}=e,a=0;a{i(e)&&!n.has(e)&&(a.delete(e),n.set(e,0),e.dispatchEvent(new(r||CustomEvent)(t))),h(e[s||"children"]||[],t,n,a)},f=new(a||MutationObserver)((e=>{for(let{length:t}=e,n=0;n{u(e),(t||(t={})).handleEvent||(t.handleEvent=n),d(e,e.addEventListener,t),o.set(e,t)},disconnect:u,kill(){f.disconnect()}}})(document,"children",CustomEvent),j=({firstChild:e})=>{if(e&&1!==e.nodeType&&!(e=e.nextElementSibling))throw"unobservable";return e},B=e=>{const{nodeType:t}=e;if(t)return 1===t?e:j(e);{const t=e.valueOf();return t!==e?B(t):j(t)}},R=(e,t)=>{const n=O(e,t);return async function(){const e=await n.apply(this,arguments);if(C(n)){const t=B(e);W.has(t)||W.connect(t,{disconnected(){x(n)}})}return e}};var D=e=>({get:t=>e.get(t),set:(t,n)=>(e.set(t,n),n)});const{isArray:z}=Array;class H extends Map{set(e,t){return super.set(e,t),t}}class P extends WeakMap{set(e,t){return super.set(e,t),t}} 4 | /*! (c) Andrea Giammarchi - ISC */const F=/^(?:area|base|br|col|embed|hr|img|input|keygen|link|menuitem|meta|param|source|track|wbr)$/i,q=/<([a-z]+[a-z0-9:._-]*)([^>]*?)(\/?)>/g,G=/([^\s\\>"'=]+)\s*=\s*(['"]?)\x01/g,I=/[\x01\x02]/g;const J=(e,t)=>111===e.nodeType?1/t<0?t?(({firstChild:e,lastChild:t})=>{const n=document.createRange();return n.setStartAfter(e),n.setEndAfter(t),n.deleteContents(),e})(e):e.lastChild:t?e.valueOf():e.firstChild:e,K=(e,t)=>{let n,s,r=t.slice(2);return!(t in e)&&(s=t.toLowerCase())in e&&(r=s.slice(2)),t=>{const s=z(t)?t:[t,!1];n!==s[0]&&(n&&e.removeEventListener(r,n,s[1]),(n=s[0])&&e.addEventListener(r,n,s[1]))}};const{isArray:Q,prototype:U}=Array,{indexOf:V}=U,{createDocumentFragment:X,createElement:Y,createElementNS:Z,createTextNode:ee,createTreeWalker:te,importNode:ne}=new Proxy(document,{get:(e,t)=>e[t].bind(e)});let se;const re=(e,t)=>t?(e=>{se||(se=Z("http://www.w3.org/2000/svg","svg")),se.innerHTML=e;const t=X();return t.append(...se.childNodes),t})(e):(e=>{const t=Y("template");return t.innerHTML=e,t.content})(e),ae=({childNodes:e},t)=>e[t],oe=(e,t,n)=>((e,t,n,s,r)=>{const a=n.length;let o=t.length,l=a,c=0,i=0,u=null;for(;cr-i){const a=s(t[c],0);for(;i{switch(t[0]){case"?":return((e,t,n)=>s=>{n!==!!s&&((n=!!s)?e.setAttribute(t,""):e.removeAttribute(t))})(e,t.slice(1),!1);case".":return((e,t)=>"dataset"===t?(({dataset:e})=>t=>{for(const n in t){const s=t[n];null==s?delete e[n]:e[n]=s}})(e):n=>{e[t]=n})(e,t.slice(1));case"@":return K(e,"on"+t.slice(1));case"o":if("n"===t[1])return K(e,t)}switch(t){case"ref":return(e=>{let t;return n=>{t!==n&&(t=n,"function"==typeof n?n(e):n.current=e)}})(e);case"aria":return(e=>t=>{for(const n in t){const s="role"===n?n:`aria-${n}`,r=t[n];null==r?e.removeAttribute(s):e.setAttribute(s,r)}})(e)}return((e,t)=>{let n,s=!0;const r=document.createAttributeNS(null,t);return t=>{if(n!==t)if(n=t,null==n)s||(e.removeAttributeNode(r),s=!0);else{const n=t;null==n?(s||e.removeAttributeNode(r),s=!0):(r.value=n,s&&(e.setAttributeNodeNS(r),s=!1))}}})(e,t)};function ce(e){const{type:t,path:n}=e,s=n.reduceRight(ae,this);return"node"===t?(e=>{let t,n,s=[];const r=a=>{switch(typeof a){case"string":case"number":case"boolean":t!==a&&(t=a,n||(n=ee("")),n.data=a,s=oe(e,s,[n]));break;case"object":case"undefined":if(null==a){t!=a&&(t=a,s=oe(e,s,[]));break}if(Q(a)){t=a,0===a.length?s=oe(e,s,[]):"object"==typeof a[0]?s=oe(e,s,a):r(String(a));break}t!==a&&"ELEMENT_NODE"in a&&(t=a,s=oe(e,s,11===a.nodeType?[...a.childNodes]:[a]));break;case"function":r(a(e))}};return r})(s):"attr"===t?le(s,e.name):(e=>{let t;return n=>{t!=n&&(t=n,e.textContent=null==n?"":n)}})(s)}const ie=e=>{const t=[];let{parentNode:n}=e;for(;n;)t.push(V.call(n.childNodes,e)),e=n,({parentNode:n}=e);return t},ue="isµ",de=new P,he=/^(?:textarea|script|style|title|plaintext|xmp)$/,pe=(e,t)=>{const n="svg"===e,s=((e,t,n)=>{let s=0;return e.join("").trim().replace(q,((e,t,s,r)=>{let a=t+s.replace(G,"=$2$1").trimEnd();return r.length&&(a+=n||F.test(t)?" /":">"})).replace(I,(e=>""===e?"\x3c!--"+t+s+++"--\x3e":t+s++))})(t,ue,n),r=re(s,n),a=te(r,129),o=[],l=t.length-1;let c=0,i=`isµ${c}`;for(;c{const{content:n,nodes:s}=de.get(t)||de.set(t,pe(e,t)),r=ne(n,!0);return{content:r,updates:s.map(ce,r)}},we=(e,{type:t,template:n,values:s})=>{const r=ye(e,s);let{entry:a}=e;a&&a.template===n&&a.type===t||(e.entry=a=((e,t)=>{const{content:n,updates:s}=fe(e,t);return{type:e,template:t,content:n,updates:s,wire:null}})(t,n));const{content:o,updates:l,wire:c}=a;for(let e=0;e{const{firstChild:t,lastChild:n}=e;if(t===n)return n||e;const{childNodes:s}=e,r=[...s];return{ELEMENT_NODE:1,nodeType:111,firstChild:t,lastChild:n,valueOf:()=>(s.length!==r.length&&e.append(...r),e)}})(o))},ye=({stack:e},t)=>{const{length:n}=t;for(let s=0;s{const t=new P;return Object.assign(((t,...n)=>new ge(e,t,n)),{for(n,s){const r=t.get(n)||t.set(n,new H);return r.get(s)||r.set(s,(t=>(n,...s)=>we(t,{type:e,template:n,values:s}))({stack:[],entry:null,wire:null}))},node:(t,...n)=>we({stack:[],entry:null,wire:null},new ge(e,t,n)).valueOf()})},ve=new P,be=me("html"),Ee=me("svg"),{create:ke}=Object,xe=(e,...t)=>new ge("html",e,t);xe.for=Oe(be);const Ce=(e,...t)=>new ge("svg",e,t);Ce.for=Oe(Ee);const Ne=D(new WeakMap),$e=(e,t)=>R((async function(){const n=await t.f.apply(this,arguments);return n instanceof ge?(await Se(e,n),t.$=_e(t,n)):t.$=n,t.$})),Me=()=>({s:[],e:null}),Ae=(e,{f:t,c:n,a:s})=>{let{e:r}=e;return r&&r.f===t||(e.e=r={f:t,h:null,$:null},r.h=$e(Me(),r)),r.h.apply(n,s)},Se=async(e,{values:t})=>{await Te(e,t)},Te=async(e,t)=>{const{s:n}=e,{length:s}=t;for(let e=0;e("svg"===t?Ee:be).for(e,t)(n,...s);function Le(e,t,n){this.f=e,this.c=t,this.a=n}function Oe(e){const t=D(new WeakMap);return(n,s)=>{const r=t.get(n)||t.set(n,ke(null)),a=r[s]||(r[s]=Me());return async(t,...r)=>(await Te(a,r),e.for(n,s)(t,...r))}}return e.Component=function(e){return function(){return new Le(e,this,arguments)}},e.createContext=e=>({_:new Set,provide:w,value:e}),e.html=xe,e.render=(e,t)=>(Ne.get(e)||Ne.set(e,{c:Me(),h:R((async function(t){const n=await("function"==typeof t?t():t);return((e,t)=>{const n="function"==typeof t?t():t,s=ve.get(e)||ve.set(e,{stack:[],entry:null,wire:null}),r=n instanceof ge?we(s,n):n;return r!==s.wire&&(s.wire=r,e.replaceChildren(r.valueOf())),e})(e,n instanceof Le?await Ae(this.c,n):(await Se(this.c,n),n))}),e)})).h(t),e.svg=Ce,e.useCallback=(e,t)=>y((()=>e),t),e.useContext=({_:e,value:t})=>(e.add(d()),t),e.useEffect=m,e.useLayoutEffect=v,e.useMemo=y,e.useReducer=(e,t,n)=>L($,M,A,E(e,t,n)),e.useRef=e=>{const t=d(),{i:n,s:s}=t;return n===s.length&&s.push({current:e}),s[t.i++]},e.useState=e=>L($,M,A,(e=>E(b,e))(e)) 5 | /*! (c) Andrea Giammarchi - ISC */,e}({}); 6 | -------------------------------------------------------------------------------- /cjs/async.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const {hooked} = require('uhooks-dom/async'); 3 | const umap = (m => /* c8 ignore start */ m.__esModule ? m.default : m /* c8 ignore stop */)(require('umap')); 4 | const {isArray} = require('uarray'); 5 | 6 | const { 7 | Hole, 8 | html: $html, 9 | svg: $svg, 10 | render: $render 11 | } = require('uhtml'); 12 | 13 | const {create} = Object; 14 | 15 | const html = (template, ...values) => new Hole('html', template, values); 16 | html.for = createFor($html); 17 | 18 | const svg = (template, ...values) => new Hole('svg', template, values); 19 | svg.for = createFor($svg); 20 | 21 | const cache = umap(new WeakMap); 22 | 23 | const render = (where, what) => ( 24 | cache.get(where) || cache.set(where, { 25 | c: createCache(), 26 | h: hooked( 27 | async function (what) { 28 | const value = await (typeof what === 'function' ? what() : what); 29 | return $render( 30 | where, 31 | value instanceof Hook ? 32 | await unroll(this.c, value) : 33 | (await unrollHole(this.c, value), value) 34 | ); 35 | }, 36 | where 37 | ) 38 | }) 39 | ).h(what); 40 | 41 | exports.Component = Component; 42 | exports.render = render; 43 | exports.html = html; 44 | exports.svg = svg; 45 | 46 | (m => { 47 | exports.createContext = m.createContext; 48 | exports.useContext = m.useContext; 49 | exports.useCallback = m.useCallback; 50 | exports.useMemo = m.useMemo; 51 | exports.useEffect = m.useEffect; 52 | exports.useLayoutEffect = m.useLayoutEffect; 53 | exports.useReducer = m.useReducer; 54 | exports.useState = m.useState; 55 | exports.useRef = m.useRef; 56 | })(require('uhooks-dom/async')); 57 | 58 | const createHook = (info, entry) => hooked(async function () { 59 | const hole = await entry.f.apply(this, arguments); 60 | if (hole instanceof Hole) { 61 | await unrollHole(info, hole); 62 | entry.$ = view(entry, hole); 63 | } 64 | else 65 | entry.$ = hole; 66 | return entry.$; 67 | }); 68 | 69 | const createCache = () => ({s: [], e: null}); 70 | 71 | const unroll = (info, {f, c, a}) => { 72 | let {e} = info; 73 | if (!e || e.f !== f) { 74 | info.e = (e = {f, h: null, $: null}); 75 | e.h = createHook(createCache(), e); 76 | } 77 | return e.h.apply(c, a); 78 | }; 79 | 80 | const unrollHole = async (info, {values}) => { 81 | await unrollValues(info, values); 82 | }; 83 | 84 | const unrollValues = async (info, values) => { 85 | const {s} = info, {length} = values; 86 | for (let i = 0; i < length; i++) { 87 | const hook = await values[i]; 88 | if (hook instanceof Hook) 89 | values[i] = await unroll(s[i] || (s[i] = createCache()), hook); 90 | else if (hook instanceof Hole) 91 | await unrollHole(s[i] || (s[i] = createCache()), hook); 92 | else if (isArray(hook)) 93 | await unrollValues(s[i] || (s[i] = createCache()), hook); 94 | else 95 | s[i] = null; 96 | } 97 | if (length < s.length) 98 | s.splice(length); 99 | }; 100 | 101 | const view = (e, {type, template, values}) => 102 | (type === 'svg' ? $svg : $html) 103 | .for(e, type)(template, ...values); 104 | 105 | function Component(f) { 106 | return function () { 107 | return new Hook(f, this, arguments); 108 | }; 109 | } 110 | 111 | function Hook(f, c, a) { 112 | this.f = f; 113 | this.c = c; 114 | this.a = a; 115 | } 116 | 117 | function createFor(uhtml) { 118 | const cache = umap(new WeakMap); 119 | return ( 120 | (e, id) => { 121 | const store = cache.get(e) || cache.set(e, create(null)); 122 | const info = store[id] || (store[id] = createCache()); 123 | return async (template, ...values) => { 124 | await unrollValues(info, values); 125 | return uhtml.for(e, id)(template, ...values); 126 | }; 127 | } 128 | ); 129 | } 130 | -------------------------------------------------------------------------------- /cjs/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const {hooked} = require('uhooks-dom'); 3 | const umap = (m => /* c8 ignore start */ m.__esModule ? m.default : m /* c8 ignore stop */)(require('umap')); 4 | const {isArray} = require('uarray'); 5 | 6 | const { 7 | Hole, 8 | html: $html, 9 | svg: $svg, 10 | render: $render 11 | } = require('uhtml'); 12 | 13 | const {create} = Object; 14 | 15 | const html = (template, ...values) => new Hole('html', template, values); 16 | html.for = createFor($html); 17 | 18 | const svg = (template, ...values) => new Hole('svg', template, values); 19 | svg.for = createFor($svg); 20 | 21 | const cache = umap(new WeakMap); 22 | 23 | const render = (where, what) => ( 24 | cache.get(where) || cache.set(where, { 25 | c: createCache(), 26 | h: hooked( 27 | /*async*/ function (what) { 28 | const value = /*await*/ (typeof what === 'function' ? what() : what); 29 | return $render( 30 | where, 31 | value instanceof Hook ? 32 | /*await*/ unroll(this.c, value) : 33 | (/*await*/ unrollHole(this.c, value), value) 34 | ); 35 | }, 36 | where 37 | ) 38 | }) 39 | ).h(what); 40 | 41 | exports.Component = Component; 42 | exports.render = render; 43 | exports.html = html; 44 | exports.svg = svg; 45 | 46 | (m => { 47 | exports.createContext = m.createContext; 48 | exports.useContext = m.useContext; 49 | exports.useCallback = m.useCallback; 50 | exports.useMemo = m.useMemo; 51 | exports.useEffect = m.useEffect; 52 | exports.useLayoutEffect = m.useLayoutEffect; 53 | exports.useReducer = m.useReducer; 54 | exports.useState = m.useState; 55 | exports.useRef = m.useRef; 56 | })(require('uhooks-dom')); 57 | 58 | const createHook = (info, entry) => hooked(/*async*/ function () { 59 | const hole = /*await*/ entry.f.apply(this, arguments); 60 | if (hole instanceof Hole) { 61 | /*await*/ unrollHole(info, hole); 62 | entry.$ = view(entry, hole); 63 | } 64 | else 65 | entry.$ = hole; 66 | return entry.$; 67 | }); 68 | 69 | const createCache = () => ({s: [], e: null}); 70 | 71 | const unroll = (info, {f, c, a}) => { 72 | let {e} = info; 73 | if (!e || e.f !== f) { 74 | info.e = (e = {f, h: null, $: null}); 75 | e.h = createHook(createCache(), e); 76 | } 77 | return e.h.apply(c, a); 78 | }; 79 | 80 | const unrollHole = /*async*/ (info, {values}) => { 81 | /*await*/ unrollValues(info, values); 82 | }; 83 | 84 | const unrollValues = /*async*/ (info, values) => { 85 | const {s} = info, {length} = values; 86 | for (let i = 0; i < length; i++) { 87 | const hook = /*await*/ values[i]; 88 | if (hook instanceof Hook) 89 | values[i] = /*await*/ unroll(s[i] || (s[i] = createCache()), hook); 90 | else if (hook instanceof Hole) 91 | /*await*/ unrollHole(s[i] || (s[i] = createCache()), hook); 92 | else if (isArray(hook)) 93 | /*await*/ unrollValues(s[i] || (s[i] = createCache()), hook); 94 | else 95 | s[i] = null; 96 | } 97 | if (length < s.length) 98 | s.splice(length); 99 | }; 100 | 101 | const view = (e, {type, template, values}) => 102 | (type === 'svg' ? $svg : $html) 103 | .for(e, type)(template, ...values); 104 | 105 | function Component(f) { 106 | return function () { 107 | return new Hook(f, this, arguments); 108 | }; 109 | } 110 | 111 | function Hook(f, c, a) { 112 | this.f = f; 113 | this.c = c; 114 | this.a = a; 115 | } 116 | 117 | function createFor(uhtml) { 118 | const cache = umap(new WeakMap); 119 | return ( 120 | (e, id) => { 121 | const store = cache.get(e) || cache.set(e, create(null)); 122 | const info = store[id] || (store[id] = createCache()); 123 | return /*async*/ (template, ...values) => { 124 | /*await*/ unrollValues(info, values); 125 | return uhtml.for(e, id)(template, ...values); 126 | }; 127 | } 128 | ); 129 | } 130 | -------------------------------------------------------------------------------- /cjs/package.json: -------------------------------------------------------------------------------- 1 | {"type":"commonjs"} -------------------------------------------------------------------------------- /cjs/x.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const {createPragma} = require('jsx2tag'); 3 | const {html} = require('./index.js'); 4 | 5 | const createElement = createPragma(html); 6 | self.React = { 7 | createElement, 8 | Fragment: createElement 9 | }; 10 | 11 | (m => Object.keys(m).map(k => k !== 'default' && (exports[k] = m[k]))) 12 | (require('jsx2tag')); 13 | (m => Object.keys(m).map(k => k !== 'default' && (exports[k] = m[k]))) 14 | (require('./index.js')); 15 | -------------------------------------------------------------------------------- /es.js: -------------------------------------------------------------------------------- 1 | self.uland=function(e){"use strict";function t(e){this.observe(e,{subtree:!0,childList:!0})}function n(e){e.type in this&&this[e.type](e)}let r=null,s=new Set;const o=e=>{const{$:t,r:n,h:r}=e;p(n)&&(c.get(r).delete(e),n()),p(e.r=t())&&c.get(r).add(e)},l=()=>{const e=s;s=new Set,e.forEach((({h:e,c:t,a:n,e:r})=>{r&&e.apply(t,n)}))},c=new WeakMap,a=[],i=[];function u(e,t){return e!==this[t]}const d=e=>{const t=c.get(e);t&&m.then((()=>{t.forEach((e=>{e.r(),e.r=null,e.d=!0})),t.clear()}))},h=()=>r,f=e=>c.has(e),p=e=>"function"==typeof e,g=e=>{const t={h:n,c:null,a:null,e:0,i:0,s:[]};return n;function n(){const n=r;r=t,t.e=t.i=0;try{return e.apply(t.c=this,t.a=arguments)}finally{r=n,a.length&&m.then(a.forEach.bind(a.splice(0),o)),i.length&&i.splice(0).forEach(o)}}},m=Promise.resolve();function y(e){const{_:t,value:n}=this;n!==e&&(this._=new Set,this.value=e,t.forEach((({h:e,c:t,a:n})=>{e.apply(t,n)})))}const v=(e,t)=>{const n=h(),{i:r,s:s}=n;return r!==s.length&&t&&!t.some(u,s[r]._)||(s[r]={$:e(),_:t}),s[n.i++].$},w=e=>(t,n)=>{const r=h(),{i:s,s:o,h:l}=r,a=s===o.length;r.i++,a&&(c.has(l)||c.set(l,new Set),o[s]={$:t,_:n,r:null,d:!1,h:l}),(a||!n||o[s].d||n.some(u,o[s]._))&&e.push(o[s]),o[s].$=t,o[s]._=n,o[s].d=!1},b=w(a),E=w(i),x=(e,t)=>p(t)?t(e):t,k=(e,t,n)=>{const r=h(),{i:o,s:c}=r;o===c.length&&c.push({$:p(n)?n(t):x(void 0,t),set:t=>{c[o].$=e(c[o].$,t),(e=>{s.has(e)||(e.e=1,s.add(e),m.then(l))})(r)}});const{$:a,set:i}=c[r.i++];return[a,i]}; 2 | /*! (c) Andrea Giammarchi - ISC */ 3 | let C=null,N=null,$=null;const A=new WeakMap,M=new WeakMap,S=(e,t,n,r)=>{const s=s=>{A.has(e)||(A.set(e,0),m.then((()=>{A.delete(e),e.apply(t,n)}))),r(s)};return M.set(r,s),s},T=(e,t,n,r)=>e?[r[0],M.get(r[1])||S(e,t,n,r[1])]:r,_=(e,t)=>{const n=g(t?function(){const[t,r,s]=[C,N,$];[C,N,$]=[n,this,arguments];try{return e.apply(N,$)}finally{[C,N,$]=[t,r,s]}}:e);return n},L=((e,r,s,o)=>{const l=new WeakMap,c=new WeakMap,a=new WeakMap,i=e=>l.has(e),u=e=>{i(e)&&(d(e,e.removeEventListener,l.get(e)),l.delete(e))},d=(e,t,n)=>{t.call(e,"disconnected",n),t.call(e,"connected",n)},h=(e,t,n,r)=>{for(let{length:s}=e,o=0;o{i(e)&&!n.has(e)&&(o.delete(e),n.set(e,0),e.dispatchEvent(new(s||CustomEvent)(t))),h(e[r||"children"]||[],t,n,o)},p=new(o||MutationObserver)((e=>{for(let{length:t}=e,n=0;n{u(e),(t||(t={})).handleEvent||(t.handleEvent=n),d(e,e.addEventListener,t),l.set(e,t)},disconnect:u,kill(){p.disconnect()}}})(document,"children",CustomEvent),O=({firstChild:e})=>{if(e&&1!==e.nodeType&&!(e=e.nextElementSibling))throw"unobservable";return e},W=e=>{const{nodeType:t}=e;if(t)return 1===t?e:O(e);{const t=e.valueOf();return t!==e?W(t):O(t)}},j=(e,t)=>{const n=_(e,t);return function(){const e=n.apply(this,arguments);if(f(n)){const t=W(e);L.has(t)||L.connect(t,{disconnected(){d(n)}})}return e}};var B=e=>({get:t=>e.get(t),set:(t,n)=>(e.set(t,n),n)});const{isArray:R}=Array;class D extends Map{set(e,t){return super.set(e,t),t}}class z extends WeakMap{set(e,t){return super.set(e,t),t}} 4 | /*! (c) Andrea Giammarchi - ISC */const H=/^(?:area|base|br|col|embed|hr|img|input|keygen|link|menuitem|meta|param|source|track|wbr)$/i,P=/<([a-z]+[a-z0-9:._-]*)([^>]*?)(\/?)>/g,F=/([^\s\\>"'=]+)\s*=\s*(['"]?)\x01/g,q=/[\x01\x02]/g;const G=(e,t)=>111===e.nodeType?1/t<0?t?(({firstChild:e,lastChild:t})=>{const n=document.createRange();return n.setStartAfter(e),n.setEndAfter(t),n.deleteContents(),e})(e):e.lastChild:t?e.valueOf():e.firstChild:e,I=(e,t)=>{let n,r,s=t.slice(2);return!(t in e)&&(r=t.toLowerCase())in e&&(s=r.slice(2)),t=>{const r=R(t)?t:[t,!1];n!==r[0]&&(n&&e.removeEventListener(s,n,r[1]),(n=r[0])&&e.addEventListener(s,n,r[1]))}};const{isArray:J,prototype:K}=Array,{indexOf:Q}=K,{createDocumentFragment:U,createElement:V,createElementNS:X,createTextNode:Y,createTreeWalker:Z,importNode:ee}=new Proxy(document,{get:(e,t)=>e[t].bind(e)});let te;const ne=(e,t)=>t?(e=>{te||(te=X("http://www.w3.org/2000/svg","svg")),te.innerHTML=e;const t=U();return t.append(...te.childNodes),t})(e):(e=>{const t=V("template");return t.innerHTML=e,t.content})(e),re=({childNodes:e},t)=>e[t],se=(e,t,n)=>((e,t,n,r,s)=>{const o=n.length;let l=t.length,c=o,a=0,i=0,u=null;for(;as-i){const o=r(t[a],0);for(;i{switch(t[0]){case"?":return((e,t,n)=>r=>{n!==!!r&&((n=!!r)?e.setAttribute(t,""):e.removeAttribute(t))})(e,t.slice(1),!1);case".":return((e,t)=>"dataset"===t?(({dataset:e})=>t=>{for(const n in t){const r=t[n];null==r?delete e[n]:e[n]=r}})(e):n=>{e[t]=n})(e,t.slice(1));case"@":return I(e,"on"+t.slice(1));case"o":if("n"===t[1])return I(e,t)}switch(t){case"ref":return(e=>{let t;return n=>{t!==n&&(t=n,"function"==typeof n?n(e):n.current=e)}})(e);case"aria":return(e=>t=>{for(const n in t){const r="role"===n?n:`aria-${n}`,s=t[n];null==s?e.removeAttribute(r):e.setAttribute(r,s)}})(e)}return((e,t)=>{let n,r=!0;const s=document.createAttributeNS(null,t);return t=>{if(n!==t)if(n=t,null==n)r||(e.removeAttributeNode(s),r=!0);else{const n=t;null==n?(r||e.removeAttributeNode(s),r=!0):(s.value=n,r&&(e.setAttributeNodeNS(s),r=!1))}}})(e,t)};function le(e){const{type:t,path:n}=e,r=n.reduceRight(re,this);return"node"===t?(e=>{let t,n,r=[];const s=o=>{switch(typeof o){case"string":case"number":case"boolean":t!==o&&(t=o,n||(n=Y("")),n.data=o,r=se(e,r,[n]));break;case"object":case"undefined":if(null==o){t!=o&&(t=o,r=se(e,r,[]));break}if(J(o)){t=o,0===o.length?r=se(e,r,[]):"object"==typeof o[0]?r=se(e,r,o):s(String(o));break}t!==o&&"ELEMENT_NODE"in o&&(t=o,r=se(e,r,11===o.nodeType?[...o.childNodes]:[o]));break;case"function":s(o(e))}};return s})(r):"attr"===t?oe(r,e.name):(e=>{let t;return n=>{t!=n&&(t=n,e.textContent=null==n?"":n)}})(r)}const ce=e=>{const t=[];let{parentNode:n}=e;for(;n;)t.push(Q.call(n.childNodes,e)),e=n,({parentNode:n}=e);return t},ae="isµ",ie=new z,ue=/^(?:textarea|script|style|title|plaintext|xmp)$/,de=(e,t)=>{const n="svg"===e,r=((e,t,n)=>{let r=0;return e.join("").trim().replace(P,((e,t,r,s)=>{let o=t+r.replace(F,"=$2$1").trimEnd();return s.length&&(o+=n||H.test(t)?" /":">"})).replace(q,(e=>""===e?"\x3c!--"+t+r+++"--\x3e":t+r++))})(t,ae,n),s=ne(r,n),o=Z(s,129),l=[],c=t.length-1;let a=0,i=`isµ${a}`;for(;a{const{content:n,nodes:r}=ie.get(t)||ie.set(t,de(e,t)),s=ee(n,!0);return{content:s,updates:r.map(le,s)}},fe=(e,{type:t,template:n,values:r})=>{const s=pe(e,r);let{entry:o}=e;o&&o.template===n&&o.type===t||(e.entry=o=((e,t)=>{const{content:n,updates:r}=he(e,t);return{type:e,template:t,content:n,updates:r,wire:null}})(t,n));const{content:l,updates:c,wire:a}=o;for(let e=0;e{const{firstChild:t,lastChild:n}=e;if(t===n)return n||e;const{childNodes:r}=e,s=[...r];return{ELEMENT_NODE:1,nodeType:111,firstChild:t,lastChild:n,valueOf:()=>(r.length!==s.length&&e.append(...s),e)}})(l))},pe=({stack:e},t)=>{const{length:n}=t;for(let r=0;r{const t=new z;return Object.assign(((t,...n)=>new ge(e,t,n)),{for(n,r){const s=t.get(n)||t.set(n,new D);return s.get(r)||s.set(r,(t=>(n,...r)=>fe(t,{type:e,template:n,values:r}))({stack:[],entry:null,wire:null}))},node:(t,...n)=>fe({stack:[],entry:null,wire:null},new ge(e,t,n)).valueOf()})},ye=new z,ve=me("html"),we=me("svg"),{create:be}=Object,Ee=(e,...t)=>new ge("html",e,t);Ee.for=_e(ve);const xe=(e,...t)=>new ge("svg",e,t);xe.for=_e(we);const ke=B(new WeakMap),Ce=(e,t)=>j((function(){const n=t.f.apply(this,arguments);return n instanceof ge?(Ae(e,n),t.$=Se(t,n)):t.$=n,t.$})),Ne=()=>({s:[],e:null}),$e=(e,{f:t,c:n,a:r})=>{let{e:s}=e;return s&&s.f===t||(e.e=s={f:t,h:null,$:null},s.h=Ce(Ne(),s)),s.h.apply(n,r)},Ae=(e,{values:t})=>{Me(e,t)},Me=(e,t)=>{const{s:n}=e,{length:r}=t;for(let e=0;e("svg"===t?we:ve).for(e,t)(n,...r);function Te(e,t,n){this.f=e,this.c=t,this.a=n}function _e(e){const t=B(new WeakMap);return(n,r)=>{const s=t.get(n)||t.set(n,be(null)),o=s[r]||(s[r]=Ne());return(t,...s)=>(Me(o,s),e.for(n,r)(t,...s))}}return e.Component=function(e){return function(){return new Te(e,this,arguments)}},e.createContext=e=>({_:new Set,provide:y,value:e}),e.html=Ee,e.render=(e,t)=>(ke.get(e)||ke.set(e,{c:Ne(),h:j((function(t){const n="function"==typeof t?t():t;return((e,t)=>{const n="function"==typeof t?t():t,r=ye.get(e)||ye.set(e,{stack:[],entry:null,wire:null}),s=n instanceof ge?fe(r,n):n;return s!==r.wire&&(r.wire=s,e.replaceChildren(s.valueOf())),e})(e,n instanceof Te?$e(this.c,n):(Ae(this.c,n),n))}),e)})).h(t),e.svg=xe,e.useCallback=(e,t)=>v((()=>e),t),e.useContext=({_:e,value:t})=>(e.add(h()),t),e.useEffect=b,e.useLayoutEffect=E,e.useMemo=v,e.useReducer=(e,t,n)=>T(C,N,$,k(e,t,n)),e.useRef=e=>{const t=h(),{i:n,s:r}=t;return n===r.length&&r.push({current:e}),r[t.i++]},e.useState=e=>T(C,N,$,(e=>k(x,e))(e)) 5 | /*! (c) Andrea Giammarchi - ISC */,e}({}); 6 | -------------------------------------------------------------------------------- /esm.js: -------------------------------------------------------------------------------- 1 | function e(e){this.observe(e,{subtree:!0,childList:!0})}function t(e){e.type in this&&this[e.type](e)}let n=null,r=new Set;const s=e=>{const{$:t,r:n,h:r}=e;p(n)&&(l.get(r).delete(e),n()),p(e.r=t())&&l.get(r).add(e)},o=()=>{const e=r;r=new Set,e.forEach((({h:e,c:t,a:n,e:r})=>{r&&e.apply(t,n)}))},l=new WeakMap,c=[],a=[];function i(e,t){return e!==this[t]}const u=e=>{const t=l.get(e);t&&g.then((()=>{t.forEach((e=>{e.r(),e.r=null,e.d=!0})),t.clear()}))},d=()=>n,h=e=>l.has(e),p=e=>"function"==typeof e,f=e=>{const t={h:r,c:null,a:null,e:0,i:0,s:[]};return r;function r(){const r=n;n=t,t.e=t.i=0;try{return e.apply(t.c=this,t.a=arguments)}finally{n=r,c.length&&g.then(c.forEach.bind(c.splice(0),s)),a.length&&a.splice(0).forEach(s)}}},g=Promise.resolve(),m=e=>({_:new Set,provide:w,value:e}),y=({_:e,value:t})=>(e.add(d()),t);function w(e){const{_:t,value:n}=this;n!==e&&(this._=new Set,this.value=e,t.forEach((({h:e,c:t,a:n})=>{e.apply(t,n)})))}const v=(e,t)=>b((()=>e),t),b=(e,t)=>{const n=d(),{i:r,s:s}=n;return r!==s.length&&t&&!t.some(i,s[r]._)||(s[r]={$:e(),_:t}),s[n.i++].$},E=e=>(t,n)=>{const r=d(),{i:s,s:o,h:c}=r,a=s===o.length;r.i++,a&&(l.has(c)||l.set(c,new Set),o[s]={$:t,_:n,r:null,d:!1,h:c}),(a||!n||o[s].d||n.some(i,o[s]._))&&e.push(o[s]),o[s].$=t,o[s]._=n,o[s].d=!1},x=E(c),k=E(a),N=(e,t)=>p(t)?t(e):t,$=(e,t,n)=>{const s=d(),{i:l,s:c}=s;l===c.length&&c.push({$:p(n)?n(t):N(void 0,t),set:t=>{c[l].$=e(c[l].$,t),(e=>{r.has(e)||(e.e=1,r.add(e),g.then(o))})(s)}});const{$:a,set:i}=c[s.i++];return[a,i]},C=e=>{const t=d(),{i:n,s:r}=t;return n===r.length&&r.push({current:e}),r[t.i++]}; 2 | /*! (c) Andrea Giammarchi - ISC */ 3 | let A=null,M=null,S=null;const T=new WeakMap,_=new WeakMap,O=(e,t,n,r)=>{const s=s=>{T.has(e)||(T.set(e,0),g.then((()=>{T.delete(e),e.apply(t,n)}))),r(s)};return _.set(r,s),s},L=(e,t,n,r)=>e?[r[0],_.get(r[1])||O(e,t,n,r[1])]:r,W=(e,t)=>{const n=f(t?function(){const[t,r,s]=[A,M,S];[A,M,S]=[n,this,arguments];try{return e.apply(M,S)}finally{[A,M,S]=[t,r,s]}}:e);return n},j=(e,t,n)=>L(A,M,S,$(e,t,n)),B=e=>L(A,M,S,(e=>$(N,e))(e)) 4 | /*! (c) Andrea Giammarchi - ISC */,D=((n,r,s,o)=>{const l=new WeakMap,c=new WeakMap,a=new WeakMap,i=e=>l.has(e),u=e=>{i(e)&&(d(e,e.removeEventListener,l.get(e)),l.delete(e))},d=(e,t,n)=>{t.call(e,"disconnected",n),t.call(e,"connected",n)},h=(e,t,n,r)=>{for(let{length:s}=e,o=0;o{i(e)&&!n.has(e)&&(o.delete(e),n.set(e,0),e.dispatchEvent(new(s||CustomEvent)(t))),h(e[r||"children"]||[],t,n,o)},f=new(o||MutationObserver)((e=>{for(let{length:t}=e,n=0;n{u(e),(n||(n={})).handleEvent||(n.handleEvent=t),d(e,e.addEventListener,n),l.set(e,n)},disconnect:u,kill(){f.disconnect()}}})(document,"children",CustomEvent),z=({firstChild:e})=>{if(e&&1!==e.nodeType&&!(e=e.nextElementSibling))throw"unobservable";return e},H=e=>{const{nodeType:t}=e;if(t)return 1===t?e:z(e);{const t=e.valueOf();return t!==e?H(t):z(t)}},P=(e,t)=>{const n=W(e,t);return function(){const e=n.apply(this,arguments);if(h(n)){const t=H(e);D.has(t)||D.connect(t,{disconnected(){u(n)}})}return e}};var R=e=>({get:t=>e.get(t),set:(t,n)=>(e.set(t,n),n)});const{isArray:F}=Array;class q extends Map{set(e,t){return super.set(e,t),t}}class G extends WeakMap{set(e,t){return super.set(e,t),t}} 5 | /*! (c) Andrea Giammarchi - ISC */const I=/^(?:area|base|br|col|embed|hr|img|input|keygen|link|menuitem|meta|param|source|track|wbr)$/i,J=/<([a-z]+[a-z0-9:._-]*)([^>]*?)(\/?)>/g,K=/([^\s\\>"'=]+)\s*=\s*(['"]?)\x01/g,Q=/[\x01\x02]/g;const U=(e,t)=>111===e.nodeType?1/t<0?t?(({firstChild:e,lastChild:t})=>{const n=document.createRange();return n.setStartAfter(e),n.setEndAfter(t),n.deleteContents(),e})(e):e.lastChild:t?e.valueOf():e.firstChild:e,V=(e,t)=>{let n,r,s=t.slice(2);return!(t in e)&&(r=t.toLowerCase())in e&&(s=r.slice(2)),t=>{const r=F(t)?t:[t,!1];n!==r[0]&&(n&&e.removeEventListener(s,n,r[1]),(n=r[0])&&e.addEventListener(s,n,r[1]))}};const{isArray:X,prototype:Y}=Array,{indexOf:Z}=Y,{createDocumentFragment:ee,createElement:te,createElementNS:ne,createTextNode:re,createTreeWalker:se,importNode:oe}=new Proxy(document,{get:(e,t)=>e[t].bind(e)});let le;const ce=(e,t)=>t?(e=>{le||(le=ne("http://www.w3.org/2000/svg","svg")),le.innerHTML=e;const t=ee();return t.append(...le.childNodes),t})(e):(e=>{const t=te("template");return t.innerHTML=e,t.content})(e),ae=({childNodes:e},t)=>e[t],ie=(e,t,n)=>((e,t,n,r,s)=>{const o=n.length;let l=t.length,c=o,a=0,i=0,u=null;for(;as-i){const o=r(t[a],0);for(;i{switch(t[0]){case"?":return((e,t,n)=>r=>{n!==!!r&&((n=!!r)?e.setAttribute(t,""):e.removeAttribute(t))})(e,t.slice(1),!1);case".":return((e,t)=>"dataset"===t?(({dataset:e})=>t=>{for(const n in t){const r=t[n];null==r?delete e[n]:e[n]=r}})(e):n=>{e[t]=n})(e,t.slice(1));case"@":return V(e,"on"+t.slice(1));case"o":if("n"===t[1])return V(e,t)}switch(t){case"ref":return(e=>{let t;return n=>{t!==n&&(t=n,"function"==typeof n?n(e):n.current=e)}})(e);case"aria":return(e=>t=>{for(const n in t){const r="role"===n?n:`aria-${n}`,s=t[n];null==s?e.removeAttribute(r):e.setAttribute(r,s)}})(e)}return((e,t)=>{let n,r=!0;const s=document.createAttributeNS(null,t);return t=>{if(n!==t)if(n=t,null==n)r||(e.removeAttributeNode(s),r=!0);else{const n=t;null==n?(r||e.removeAttributeNode(s),r=!0):(s.value=n,r&&(e.setAttributeNodeNS(s),r=!1))}}})(e,t)};function de(e){const{type:t,path:n}=e,r=n.reduceRight(ae,this);return"node"===t?(e=>{let t,n,r=[];const s=o=>{switch(typeof o){case"string":case"number":case"boolean":t!==o&&(t=o,n||(n=re("")),n.data=o,r=ie(e,r,[n]));break;case"object":case"undefined":if(null==o){t!=o&&(t=o,r=ie(e,r,[]));break}if(X(o)){t=o,0===o.length?r=ie(e,r,[]):"object"==typeof o[0]?r=ie(e,r,o):s(String(o));break}t!==o&&"ELEMENT_NODE"in o&&(t=o,r=ie(e,r,11===o.nodeType?[...o.childNodes]:[o]));break;case"function":s(o(e))}};return s})(r):"attr"===t?ue(r,e.name):(e=>{let t;return n=>{t!=n&&(t=n,e.textContent=null==n?"":n)}})(r)}const he=e=>{const t=[];let{parentNode:n}=e;for(;n;)t.push(Z.call(n.childNodes,e)),e=n,({parentNode:n}=e);return t},pe=new G,fe=/^(?:textarea|script|style|title|plaintext|xmp)$/,ge=(e,t)=>{const n="svg"===e,r=((e,t,n)=>{let r=0;return e.join("").trim().replace(J,((e,t,r,s)=>{let o=t+r.replace(K,"=$2$1").trimEnd();return s.length&&(o+=n||I.test(t)?" /":">"})).replace(Q,(e=>""===e?"\x3c!--"+t+r+++"--\x3e":t+r++))})(t,"isµ",n),s=ce(r,n),o=se(s,129),l=[],c=t.length-1;let a=0,i=`isµ${a}`;for(;a{const{content:n,nodes:r}=pe.get(t)||pe.set(t,ge(e,t)),s=oe(n,!0);return{content:s,updates:r.map(de,s)}},ye=(e,{type:t,template:n,values:r})=>{const s=we(e,r);let{entry:o}=e;o&&o.template===n&&o.type===t||(e.entry=o=((e,t)=>{const{content:n,updates:r}=me(e,t);return{type:e,template:t,content:n,updates:r,wire:null}})(t,n));const{content:l,updates:c,wire:a}=o;for(let e=0;e{const{firstChild:t,lastChild:n}=e;if(t===n)return n||e;const{childNodes:r}=e,s=[...r];return{ELEMENT_NODE:1,nodeType:111,firstChild:t,lastChild:n,valueOf:()=>(r.length!==s.length&&e.append(...s),e)}})(l))},we=({stack:e},t)=>{const{length:n}=t;for(let r=0;r{const t=new G;return Object.assign(((t,...n)=>new ve(e,t,n)),{for(n,r){const s=t.get(n)||t.set(n,new q);return s.get(r)||s.set(r,(t=>(n,...r)=>ye(t,{type:e,template:n,values:r}))({stack:[],entry:null,wire:null}))},node:(t,...n)=>ye({stack:[],entry:null,wire:null},new ve(e,t,n)).valueOf()})},Ee=new G,xe=be("html"),ke=be("svg"),{create:Ne}=Object,$e=(e,...t)=>new ve("html",e,t);$e.for=De(xe);const Ce=(e,...t)=>new ve("svg",e,t);Ce.for=De(ke);const Ae=R(new WeakMap),Me=(e,t)=>(Ae.get(e)||Ae.set(e,{c:Te(),h:P((function(t){const n="function"==typeof t?t():t;return((e,t)=>{const n="function"==typeof t?t():t,r=Ee.get(e)||Ee.set(e,{stack:[],entry:null,wire:null}),s=n instanceof ve?ye(r,n):n;return s!==r.wire&&(r.wire=s,e.replaceChildren(s.valueOf())),e})(e,n instanceof Be?_e(this.c,n):(Oe(this.c,n),n))}),e)})).h(t),Se=(e,t)=>P((function(){const n=t.f.apply(this,arguments);return n instanceof ve?(Oe(e,n),t.$=We(t,n)):t.$=n,t.$})),Te=()=>({s:[],e:null}),_e=(e,{f:t,c:n,a:r})=>{let{e:s}=e;return s&&s.f===t||(e.e=s={f:t,h:null,$:null},s.h=Se(Te(),s)),s.h.apply(n,r)},Oe=(e,{values:t})=>{Le(e,t)},Le=(e,t)=>{const{s:n}=e,{length:r}=t;for(let e=0;e("svg"===t?ke:xe).for(e,t)(n,...r);function je(e){return function(){return new Be(e,this,arguments)}}function Be(e,t,n){this.f=e,this.c=t,this.a=n}function De(e){const t=R(new WeakMap);return(n,r)=>{const s=t.get(n)||t.set(n,Ne(null)),o=s[r]||(s[r]=Te());return(t,...s)=>(Le(o,s),e.for(n,r)(t,...s))}}export{je as Component,m as createContext,$e as html,Me as render,Ce as svg,v as useCallback,y as useContext,x as useEffect,k as useLayoutEffect,b as useMemo,j as useReducer,C as useRef,B as useState}; 6 | -------------------------------------------------------------------------------- /esm/async.js: -------------------------------------------------------------------------------- 1 | import {hooked} from 'uhooks-dom/async'; 2 | import umap from 'umap'; 3 | import {isArray} from 'uarray'; 4 | 5 | import { 6 | Hole, 7 | html as $html, 8 | svg as $svg, 9 | render as $render 10 | } from 'uhtml'; 11 | 12 | const {create} = Object; 13 | 14 | const html = (template, ...values) => new Hole('html', template, values); 15 | html.for = createFor($html); 16 | 17 | const svg = (template, ...values) => new Hole('svg', template, values); 18 | svg.for = createFor($svg); 19 | 20 | const cache = umap(new WeakMap); 21 | 22 | const render = (where, what) => ( 23 | cache.get(where) || cache.set(where, { 24 | c: createCache(), 25 | h: hooked( 26 | async function (what) { 27 | const value = await (typeof what === 'function' ? what() : what); 28 | return $render( 29 | where, 30 | value instanceof Hook ? 31 | await unroll(this.c, value) : 32 | (await unrollHole(this.c, value), value) 33 | ); 34 | }, 35 | where 36 | ) 37 | }) 38 | ).h(what); 39 | 40 | export {Component, render, html, svg}; 41 | 42 | export { 43 | createContext, useContext, 44 | useCallback, useMemo, 45 | useEffect, useLayoutEffect, 46 | useReducer, useState, useRef 47 | } from 'uhooks-dom/async'; 48 | 49 | const createHook = (info, entry) => hooked(async function () { 50 | const hole = await entry.f.apply(this, arguments); 51 | if (hole instanceof Hole) { 52 | await unrollHole(info, hole); 53 | entry.$ = view(entry, hole); 54 | } 55 | else 56 | entry.$ = hole; 57 | return entry.$; 58 | }); 59 | 60 | const createCache = () => ({s: [], e: null}); 61 | 62 | const unroll = (info, {f, c, a}) => { 63 | let {e} = info; 64 | if (!e || e.f !== f) { 65 | info.e = (e = {f, h: null, $: null}); 66 | e.h = createHook(createCache(), e); 67 | } 68 | return e.h.apply(c, a); 69 | }; 70 | 71 | const unrollHole = async (info, {values}) => { 72 | await unrollValues(info, values); 73 | }; 74 | 75 | const unrollValues = async (info, values) => { 76 | const {s} = info, {length} = values; 77 | for (let i = 0; i < length; i++) { 78 | const hook = await values[i]; 79 | if (hook instanceof Hook) 80 | values[i] = await unroll(s[i] || (s[i] = createCache()), hook); 81 | else if (hook instanceof Hole) 82 | await unrollHole(s[i] || (s[i] = createCache()), hook); 83 | else if (isArray(hook)) 84 | await unrollValues(s[i] || (s[i] = createCache()), hook); 85 | else 86 | s[i] = null; 87 | } 88 | if (length < s.length) 89 | s.splice(length); 90 | }; 91 | 92 | const view = (e, {type, template, values}) => 93 | (type === 'svg' ? $svg : $html) 94 | .for(e, type)(template, ...values); 95 | 96 | function Component(f) { 97 | return function () { 98 | return new Hook(f, this, arguments); 99 | }; 100 | } 101 | 102 | function Hook(f, c, a) { 103 | this.f = f; 104 | this.c = c; 105 | this.a = a; 106 | } 107 | 108 | function createFor(uhtml) { 109 | const cache = umap(new WeakMap); 110 | return ( 111 | (e, id) => { 112 | const store = cache.get(e) || cache.set(e, create(null)); 113 | const info = store[id] || (store[id] = createCache()); 114 | return async (template, ...values) => { 115 | await unrollValues(info, values); 116 | return uhtml.for(e, id)(template, ...values); 117 | }; 118 | } 119 | ); 120 | } 121 | -------------------------------------------------------------------------------- /esm/index.js: -------------------------------------------------------------------------------- 1 | import {hooked} from 'uhooks-dom'; 2 | import umap from 'umap'; 3 | import {isArray} from 'uarray'; 4 | 5 | import { 6 | Hole, 7 | html as $html, 8 | svg as $svg, 9 | render as $render 10 | } from 'uhtml'; 11 | 12 | const {create} = Object; 13 | 14 | const html = (template, ...values) => new Hole('html', template, values); 15 | html.for = createFor($html); 16 | 17 | const svg = (template, ...values) => new Hole('svg', template, values); 18 | svg.for = createFor($svg); 19 | 20 | const cache = umap(new WeakMap); 21 | 22 | const render = (where, what) => ( 23 | cache.get(where) || cache.set(where, { 24 | c: createCache(), 25 | h: hooked( 26 | /*async*/ function (what) { 27 | const value = /*await*/ (typeof what === 'function' ? what() : what); 28 | return $render( 29 | where, 30 | value instanceof Hook ? 31 | /*await*/ unroll(this.c, value) : 32 | (/*await*/ unrollHole(this.c, value), value) 33 | ); 34 | }, 35 | where 36 | ) 37 | }) 38 | ).h(what); 39 | 40 | export {Component, render, html, svg}; 41 | 42 | export { 43 | createContext, useContext, 44 | useCallback, useMemo, 45 | useEffect, useLayoutEffect, 46 | useReducer, useState, useRef 47 | } from 'uhooks-dom'; 48 | 49 | const createHook = (info, entry) => hooked(/*async*/ function () { 50 | const hole = /*await*/ entry.f.apply(this, arguments); 51 | if (hole instanceof Hole) { 52 | /*await*/ unrollHole(info, hole); 53 | entry.$ = view(entry, hole); 54 | } 55 | else 56 | entry.$ = hole; 57 | return entry.$; 58 | }); 59 | 60 | const createCache = () => ({s: [], e: null}); 61 | 62 | const unroll = (info, {f, c, a}) => { 63 | let {e} = info; 64 | if (!e || e.f !== f) { 65 | info.e = (e = {f, h: null, $: null}); 66 | e.h = createHook(createCache(), e); 67 | } 68 | return e.h.apply(c, a); 69 | }; 70 | 71 | const unrollHole = /*async*/ (info, {values}) => { 72 | /*await*/ unrollValues(info, values); 73 | }; 74 | 75 | const unrollValues = /*async*/ (info, values) => { 76 | const {s} = info, {length} = values; 77 | for (let i = 0; i < length; i++) { 78 | const hook = /*await*/ values[i]; 79 | if (hook instanceof Hook) 80 | values[i] = /*await*/ unroll(s[i] || (s[i] = createCache()), hook); 81 | else if (hook instanceof Hole) 82 | /*await*/ unrollHole(s[i] || (s[i] = createCache()), hook); 83 | else if (isArray(hook)) 84 | /*await*/ unrollValues(s[i] || (s[i] = createCache()), hook); 85 | else 86 | s[i] = null; 87 | } 88 | if (length < s.length) 89 | s.splice(length); 90 | }; 91 | 92 | const view = (e, {type, template, values}) => 93 | (type === 'svg' ? $svg : $html) 94 | .for(e, type)(template, ...values); 95 | 96 | function Component(f) { 97 | return function () { 98 | return new Hook(f, this, arguments); 99 | }; 100 | } 101 | 102 | function Hook(f, c, a) { 103 | this.f = f; 104 | this.c = c; 105 | this.a = a; 106 | } 107 | 108 | function createFor(uhtml) { 109 | const cache = umap(new WeakMap); 110 | return ( 111 | (e, id) => { 112 | const store = cache.get(e) || cache.set(e, create(null)); 113 | const info = store[id] || (store[id] = createCache()); 114 | return /*async*/ (template, ...values) => { 115 | /*await*/ unrollValues(info, values); 116 | return uhtml.for(e, id)(template, ...values); 117 | }; 118 | } 119 | ); 120 | } 121 | -------------------------------------------------------------------------------- /esm/x.js: -------------------------------------------------------------------------------- 1 | import {createPragma} from 'jsx2tag'; 2 | import {html} from './index.js'; 3 | 4 | const createElement = createPragma(html); 5 | self.React = { 6 | createElement, 7 | Fragment: createElement 8 | }; 9 | 10 | export * from 'jsx2tag'; 11 | export * from './index.js'; 12 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @typedef {{ 3 | * (...args: any[]): Hole; 4 | * for: (entry: IEntry, id?: string) => (...args: any[]) => any 5 | * }} IRenderer 6 | */ 7 | /** 8 | * @type {IRenderer} 9 | */ 10 | export function html(...args: any[]): Hole; 11 | export namespace html { } 12 | /** 13 | * @type {IRenderer} 14 | */ 15 | export function svg(...args: any[]): Hole; 16 | export namespace svg { } 17 | export function Component(fn: (...args: Args[]) => unknown): (...args: Args[]) => Hook; 18 | export function render(where: Node, what: any): Node; 19 | export type IRenderer = { 20 | (...args: any[]): Hole; 21 | for: (entry: IEntry, id?: string) => (...args: any[]) => any; 22 | }; 23 | export type ITagFunction = (template: TemplateStringsArray, ...values: any[]) => K; 24 | /** 25 | * An interface describing hooks counter 26 | */ 27 | export type ICounter = { 28 | a: number; 29 | aLength: number; 30 | i: number; 31 | iLength: number; 32 | }; 33 | /** 34 | * An interface describing hooks info 35 | */ 36 | export type IInfo = { 37 | sub?: IInfo[]; 38 | stack: IEntry[]; 39 | }; 40 | export type IEntry = { 41 | hook: any; 42 | fn: any; 43 | }; 44 | export type CacheFn = (wm: any, key: any, value: T) => T; 45 | import { Hole } from "lighterhtml"; 46 | /** 47 | * @class 48 | * @param {Function} fn 49 | * @param {any[]} args 50 | */ 51 | declare function Hook(fn: Function, args: any[]): void; 52 | declare class Hook { 53 | /** 54 | * @class 55 | * @param {Function} fn 56 | * @param {any[]} args 57 | */ 58 | constructor(fn: Function, args: any[]); 59 | fn: Function; 60 | args: any[]; 61 | } 62 | export { useState, useEffect, useContext, createContext, useRef, useReducer, useCallback, useMemo, useLayoutEffect } from "uhooks-dom"; 63 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | self.uland = (function (exports) { 2 | 'use strict'; 3 | 4 | /** 5 | * @typedef {Object} Handler an object that handle events. 6 | * @property {(event: Event) => void} connected an optional method triggered when node is connected. 7 | * @property {(event: Event) => void} disconnected an optional method triggered when node is disconnected. 8 | */ 9 | 10 | /** 11 | * @typedef {Object} UConnect an utility to connect or disconnect nodes to observe. 12 | * @property {(node: Node, handler?: Handler) => void} connect a method to start observing a generic Node. 13 | * @property {(node: Node) => void} disconnect a method to stop observing a generic Node. 14 | * @property {() => void} kill a method to kill/disconnect the MutationObserver. 15 | */ 16 | 17 | /** 18 | * Attach a MutationObserver to a generic node and returns a UConnect instance. 19 | * @param {Node} root a DOM node to observe for mutations. 20 | * @param {string} parse the kind of nodes to parse: children, by default, or childNodes. 21 | * @param {Event} CE an Event/CustomEvent constructor (polyfilled in SSR). 22 | * @param {MutationObserver} MO a MutationObserver constructor (polyfilled in SSR). 23 | * @returns {UConnect} an utility to connect or disconnect nodes to observe. 24 | */ 25 | const observe = (root, parse, CE, MO) => { 26 | const observed = new WeakMap; 27 | 28 | // these two should be WeakSet but IE11 happens 29 | const wmin = new WeakMap; 30 | const wmout = new WeakMap; 31 | 32 | const has = node => observed.has(node); 33 | const disconnect = node => { 34 | if (has(node)) { 35 | listener(node, node.removeEventListener, observed.get(node)); 36 | observed.delete(node); 37 | } 38 | }; 39 | const connect = (node, handler) => { 40 | disconnect(node); 41 | if (!(handler || (handler = {})).handleEvent) 42 | handler.handleEvent = handleEvent; 43 | listener(node, node.addEventListener, handler); 44 | observed.set(node, handler); 45 | }; 46 | 47 | const listener = (node, method, handler) => { 48 | method.call(node, 'disconnected', handler); 49 | method.call(node, 'connected', handler); 50 | }; 51 | 52 | const notifyObserved = (nodes, type, wmin, wmout) => { 53 | for (let {length} = nodes, i = 0; i < length; i++) 54 | notifyTarget(nodes[i], type, wmin, wmout); 55 | }; 56 | 57 | const notifyTarget = (node, type, wmin, wmout) => { 58 | if (has(node) && !wmin.has(node)) { 59 | wmout.delete(node); 60 | wmin.set(node, 0); 61 | node.dispatchEvent(new (CE || CustomEvent)(type)); 62 | } 63 | notifyObserved(node[parse || 'children'] || [], type, wmin, wmout); 64 | }; 65 | 66 | const mo = new (MO || MutationObserver)(nodes => { 67 | for (let {length} = nodes, i = 0; i < length; i++) { 68 | const {removedNodes, addedNodes} = nodes[i]; 69 | notifyObserved(removedNodes, 'disconnected', wmout, wmin); 70 | notifyObserved(addedNodes, 'connected', wmin, wmout); 71 | } 72 | }); 73 | 74 | mo.add = add; 75 | mo.add(root || document); 76 | 77 | const {attachShadow} = Element.prototype; 78 | if (attachShadow) 79 | Element.prototype.attachShadow = function (init) { 80 | const sd = attachShadow.call(this, init); 81 | mo.add(sd); 82 | return sd; 83 | }; 84 | 85 | return {has, connect, disconnect, kill() { mo.disconnect(); }}; 86 | }; 87 | 88 | function add(node) { 89 | this.observe(node, {subtree: true, childList: true}); 90 | } 91 | 92 | function handleEvent(event) { 93 | if (event.type in this) 94 | this[event.type](event); 95 | } 96 | 97 | let info = null, schedule = new Set; 98 | 99 | const invoke = effect => { 100 | const {$, r, h} = effect; 101 | if (isFunction(r)) { 102 | fx$1.get(h).delete(effect); 103 | r(); 104 | } 105 | if (isFunction(effect.r = $())) 106 | fx$1.get(h).add(effect); 107 | }; 108 | 109 | const runSchedule = () => { 110 | const previous = schedule; 111 | schedule = new Set; 112 | previous.forEach(({h, c, a, e}) => { 113 | // avoid running schedules when the hook is 114 | // re-executed before such schedule happens 115 | if (e) 116 | h.apply(c, a); 117 | }); 118 | }; 119 | 120 | const fx$1 = new WeakMap; 121 | const effects = []; 122 | const layoutEffects = []; 123 | 124 | function different(value, i) { 125 | return value !== this[i]; 126 | } 127 | const dropEffect = hook => { 128 | const effects = fx$1.get(hook); 129 | if (effects) 130 | wait.then(() => { 131 | effects.forEach(effect => { 132 | effect.r(); 133 | effect.r = null; 134 | effect.d = true; 135 | }); 136 | effects.clear(); 137 | }); 138 | }; 139 | 140 | const getInfo = () => info; 141 | 142 | const hasEffect = hook => fx$1.has(hook); 143 | 144 | const isFunction = f => typeof f === 'function'; 145 | 146 | const hooked$2 = callback => { 147 | const current = {h: hook, c: null, a: null, e: 0, i: 0, s: []}; 148 | return hook; 149 | function hook() { 150 | const prev = info; 151 | info = current; 152 | current.e = current.i = 0; 153 | try { 154 | return callback.apply(current.c = this, current.a = arguments); 155 | } 156 | finally { 157 | info = prev; 158 | if (effects.length) 159 | wait.then(effects.forEach.bind(effects.splice(0), invoke)); 160 | if (layoutEffects.length) 161 | layoutEffects.splice(0).forEach(invoke); 162 | } 163 | } 164 | }; 165 | 166 | const reschedule = info => { 167 | if (!schedule.has(info)) { 168 | info.e = 1; 169 | schedule.add(info); 170 | wait.then(runSchedule); 171 | } 172 | }; 173 | 174 | const wait = Promise.resolve(); 175 | 176 | const createContext = value => ({ 177 | _: new Set, 178 | provide, 179 | value 180 | }); 181 | 182 | const useContext = ({_, value}) => { 183 | _.add(getInfo()); 184 | return value; 185 | }; 186 | 187 | function provide(newValue) { 188 | const {_, value} = this; 189 | if (value !== newValue) { 190 | this._ = new Set; 191 | this.value = newValue; 192 | _.forEach(({h, c, a}) => { 193 | h.apply(c, a); 194 | }); 195 | } 196 | } 197 | 198 | const useCallback = (fn, guards) => useMemo(() => fn, guards); 199 | 200 | const useMemo = (memo, guards) => { 201 | const info = getInfo(); 202 | const {i, s} = info; 203 | if (i === s.length || !guards || guards.some(different, s[i]._)) 204 | s[i] = {$: memo(), _: guards}; 205 | return s[info.i++].$; 206 | }; 207 | 208 | const createEffect = stack => (callback, guards) => { 209 | const info = getInfo(); 210 | const {i, s, h} = info; 211 | const call = i === s.length; 212 | info.i++; 213 | if (call) { 214 | if (!fx$1.has(h)) 215 | fx$1.set(h, new Set); 216 | s[i] = {$: callback, _: guards, r: null, d: false, h}; 217 | } 218 | if (call || !guards || s[i].d || guards.some(different, s[i]._)) 219 | stack.push(s[i]); 220 | s[i].$ = callback; 221 | s[i]._ = guards; 222 | s[i].d = false; 223 | }; 224 | 225 | const useEffect = createEffect(effects); 226 | 227 | const useLayoutEffect = createEffect(layoutEffects); 228 | 229 | const getValue = (value, f) => isFunction(f) ? f(value) : f; 230 | 231 | const useReducer$1 = (reducer, value, init) => { 232 | const info = getInfo(); 233 | const {i, s} = info; 234 | if (i === s.length) 235 | s.push({ 236 | $: isFunction(init) ? 237 | init(value) : getValue(void 0, value), 238 | set: value => { 239 | s[i].$ = reducer(s[i].$, value); 240 | reschedule(info); 241 | } 242 | }); 243 | const {$, set} = s[info.i++]; 244 | return [$, set]; 245 | }; 246 | 247 | const useState$1 = value => useReducer$1(getValue, value); 248 | 249 | const useRef = current => { 250 | const info = getInfo(); 251 | const {i, s} = info; 252 | if (i === s.length) 253 | s.push({current}); 254 | return s[info.i++]; 255 | }; 256 | 257 | /*! (c) Andrea Giammarchi - ISC */ 258 | 259 | let h = null, c = null, a = null; 260 | 261 | const fx = new WeakMap; 262 | const states = new WeakMap; 263 | 264 | const set = (h, c, a, update) => { 265 | const wrap = value => { 266 | if (!fx.has(h)) { 267 | fx.set(h, 0); 268 | wait.then(() => { 269 | fx.delete(h); 270 | h.apply(c, a); 271 | }); 272 | } 273 | update(value); 274 | }; 275 | states.set(update, wrap); 276 | return wrap; 277 | }; 278 | 279 | const wrap = (h, c, a, state) => ( 280 | h ? [ 281 | state[0], 282 | states.get(state[1]) || set(h, c, a, state[1]) 283 | ] : 284 | state 285 | ); 286 | 287 | const hooked$1 = (callback, outer) => { 288 | const hook = hooked$2( 289 | outer ? 290 | /*async*/ function () { 291 | const [ph, pc, pa] = [h, c, a]; 292 | [h, c, a] = [hook, this, arguments]; 293 | try { 294 | return /*await*/ callback.apply(c, a); 295 | } 296 | finally { 297 | [h, c, a] = [ph, pc, pa]; 298 | } 299 | } : 300 | callback 301 | ); 302 | return hook; 303 | }; 304 | 305 | const useReducer = (reducer, value, init) => 306 | wrap(h, c, a, useReducer$1(reducer, value, init)); 307 | 308 | const useState = value => wrap(h, c, a, useState$1(value)); 309 | 310 | /*! (c) Andrea Giammarchi - ISC */ 311 | const observer = observe(document, 'children', CustomEvent); 312 | 313 | const find = ({firstChild}) => { 314 | if ( 315 | firstChild && 316 | firstChild.nodeType !== 1 && 317 | !(firstChild = firstChild.nextElementSibling) 318 | ) 319 | throw 'unobservable'; 320 | return firstChild; 321 | }; 322 | 323 | const get = node => { 324 | const {nodeType} = node; 325 | if (nodeType) 326 | return nodeType === 1 ? node : find(node); 327 | else { 328 | // give a chance to facades to return a reasonable value 329 | const value = node.valueOf(); 330 | return value !== node ? get(value) : find(value); 331 | } 332 | }; 333 | 334 | const hooked = (fn, outer) => { 335 | const hook = hooked$1(fn, outer); 336 | return /*async*/ function () { 337 | const node = /*await*/ hook.apply(this, arguments); 338 | if (hasEffect(hook)) { 339 | const element = get(node); 340 | if (!observer.has(element)) 341 | observer.connect(element, { 342 | disconnected() { 343 | dropEffect(hook); 344 | } 345 | }); 346 | } 347 | return node; 348 | }; 349 | }; 350 | 351 | var umap = _ => ({ 352 | // About: get: _.get.bind(_) 353 | // It looks like WebKit/Safari didn't optimize bind at all, 354 | // so that using bind slows it down by 60%. 355 | // Firefox and Chrome are just fine in both cases, 356 | // so let's use the approach that works fast everywhere 👍 357 | get: key => _.get(key), 358 | set: (key, value) => (_.set(key, value), value) 359 | }); 360 | 361 | const {isArray: isArray$1} = Array; 362 | 363 | class MapSet extends Map { 364 | set(key, value) { 365 | super.set(key, value); 366 | return value; 367 | } 368 | } 369 | 370 | class WeakMapSet extends WeakMap { 371 | set(key, value) { 372 | super.set(key, value); 373 | return value; 374 | } 375 | } 376 | 377 | /*! (c) Andrea Giammarchi - ISC */ 378 | const empty = /^(?:area|base|br|col|embed|hr|img|input|keygen|link|menuitem|meta|param|source|track|wbr)$/i; 379 | const elements = /<([a-z]+[a-z0-9:._-]*)([^>]*?)(\/?)>/g; 380 | const attributes = /([^\s\\>"'=]+)\s*=\s*(['"]?)\x01/g; 381 | const holes = /[\x01\x02]/g; 382 | 383 | // \x01 Node.ELEMENT_NODE 384 | // \x02 Node.ATTRIBUTE_NODE 385 | 386 | /** 387 | * Given a template, find holes as both nodes and attributes and 388 | * return a string with holes as either comment nodes or named attributes. 389 | * @param {string[]} template a template literal tag array 390 | * @param {string} prefix prefix to use per each comment/attribute 391 | * @param {boolean} svg enforces self-closing tags 392 | * @returns {string} X/HTML with prefixed comments or attributes 393 | */ 394 | var instrument = (template, prefix, svg) => { 395 | let i = 0; 396 | return template 397 | .join('\x01') 398 | .trim() 399 | .replace( 400 | elements, 401 | (_, name, attrs, selfClosing) => { 402 | let ml = name + attrs.replace(attributes, '\x02=$2$1').trimEnd(); 403 | if (selfClosing.length) 404 | ml += (svg || empty.test(name)) ? ' /' : ('>'; 406 | } 407 | ) 408 | .replace( 409 | holes, 410 | hole => hole === '\x01' ? 411 | ('') : 412 | (prefix + i++) 413 | ); 414 | }; 415 | 416 | const ELEMENT_NODE = 1; 417 | const nodeType = 111; 418 | 419 | const remove = ({firstChild, lastChild}) => { 420 | const range = document.createRange(); 421 | range.setStartAfter(firstChild); 422 | range.setEndAfter(lastChild); 423 | range.deleteContents(); 424 | return firstChild; 425 | }; 426 | 427 | const diffable = (node, operation) => node.nodeType === nodeType ? 428 | ((1 / operation) < 0 ? 429 | (operation ? remove(node) : node.lastChild) : 430 | (operation ? node.valueOf() : node.firstChild)) : 431 | node 432 | ; 433 | 434 | const persistent = fragment => { 435 | const {firstChild, lastChild} = fragment; 436 | if (firstChild === lastChild) 437 | return lastChild || fragment; 438 | const {childNodes} = fragment; 439 | const nodes = [...childNodes]; 440 | return { 441 | ELEMENT_NODE, 442 | nodeType, 443 | firstChild, 444 | lastChild, 445 | valueOf() { 446 | if (childNodes.length !== nodes.length) 447 | fragment.append(...nodes); 448 | return fragment; 449 | } 450 | }; 451 | }; 452 | 453 | const aria = node => values => { 454 | for (const key in values) { 455 | const name = key === 'role' ? key : `aria-${key}`; 456 | const value = values[key]; 457 | if (value == null) 458 | node.removeAttribute(name); 459 | else 460 | node.setAttribute(name, value); 461 | } 462 | }; 463 | 464 | const attribute = (node, name) => { 465 | let oldValue, orphan = true; 466 | const attributeNode = document.createAttributeNS(null, name); 467 | return newValue => { 468 | if (oldValue !== newValue) { 469 | oldValue = newValue; 470 | if (oldValue == null) { 471 | if (!orphan) { 472 | node.removeAttributeNode(attributeNode); 473 | orphan = true; 474 | } 475 | } 476 | else { 477 | const value = newValue; 478 | if (value == null) { 479 | if (!orphan) 480 | node.removeAttributeNode(attributeNode); 481 | orphan = true; 482 | } 483 | else { 484 | attributeNode.value = value; 485 | if (orphan) { 486 | node.setAttributeNodeNS(attributeNode); 487 | orphan = false; 488 | } 489 | } 490 | } 491 | } 492 | }; 493 | }; 494 | 495 | const boolean = (node, key, oldValue) => newValue => { 496 | if (oldValue !== !!newValue) { 497 | // when IE won't be around anymore ... 498 | // node.toggleAttribute(key, oldValue = !!newValue); 499 | if ((oldValue = !!newValue)) 500 | node.setAttribute(key, ''); 501 | else 502 | node.removeAttribute(key); 503 | } 504 | }; 505 | 506 | const data = ({dataset}) => values => { 507 | for (const key in values) { 508 | const value = values[key]; 509 | if (value == null) 510 | delete dataset[key]; 511 | else 512 | dataset[key] = value; 513 | } 514 | }; 515 | 516 | const event = (node, name) => { 517 | let oldValue, lower, type = name.slice(2); 518 | if (!(name in node) && (lower = name.toLowerCase()) in node) 519 | type = lower.slice(2); 520 | return newValue => { 521 | const info = isArray$1(newValue) ? newValue : [newValue, false]; 522 | if (oldValue !== info[0]) { 523 | if (oldValue) 524 | node.removeEventListener(type, oldValue, info[1]); 525 | if (oldValue = info[0]) 526 | node.addEventListener(type, oldValue, info[1]); 527 | } 528 | }; 529 | }; 530 | 531 | const ref = node => { 532 | let oldValue; 533 | return value => { 534 | if (oldValue !== value) { 535 | oldValue = value; 536 | if (typeof value === 'function') 537 | value(node); 538 | else 539 | value.current = node; 540 | } 541 | }; 542 | }; 543 | 544 | const setter = (node, key) => key === 'dataset' ? 545 | data(node) : 546 | value => { 547 | node[key] = value; 548 | }; 549 | 550 | const text = node => { 551 | let oldValue; 552 | return newValue => { 553 | if (oldValue != newValue) { 554 | oldValue = newValue; 555 | node.textContent = newValue == null ? '' : newValue; 556 | } 557 | }; 558 | }; 559 | 560 | /** 561 | * ISC License 562 | * 563 | * Copyright (c) 2020, Andrea Giammarchi, @WebReflection 564 | * 565 | * Permission to use, copy, modify, and/or distribute this software for any 566 | * purpose with or without fee is hereby granted, provided that the above 567 | * copyright notice and this permission notice appear in all copies. 568 | * 569 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 570 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 571 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 572 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 573 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 574 | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 575 | * PERFORMANCE OF THIS SOFTWARE. 576 | */ 577 | 578 | /** 579 | * @param {Node} parentNode The container where children live 580 | * @param {Node[]} a The list of current/live children 581 | * @param {Node[]} b The list of future children 582 | * @param {(entry: Node, action: number) => Node} get 583 | * The callback invoked per each entry related DOM operation. 584 | * @param {Node} [before] The optional node used as anchor to insert before. 585 | * @returns {Node[]} The same list of future children. 586 | */ 587 | var udomdiff = (parentNode, a, b, get, before) => { 588 | const bLength = b.length; 589 | let aEnd = a.length; 590 | let bEnd = bLength; 591 | let aStart = 0; 592 | let bStart = 0; 593 | let map = null; 594 | while (aStart < aEnd || bStart < bEnd) { 595 | // append head, tail, or nodes in between: fast path 596 | if (aEnd === aStart) { 597 | // we could be in a situation where the rest of nodes that 598 | // need to be added are not at the end, and in such case 599 | // the node to `insertBefore`, if the index is more than 0 600 | // must be retrieved, otherwise it's gonna be the first item. 601 | const node = bEnd < bLength ? 602 | (bStart ? 603 | (get(b[bStart - 1], -0).nextSibling) : 604 | get(b[bEnd - bStart], 0)) : 605 | before; 606 | while (bStart < bEnd) 607 | parentNode.insertBefore(get(b[bStart++], 1), node); 608 | } 609 | // remove head or tail: fast path 610 | else if (bEnd === bStart) { 611 | while (aStart < aEnd) { 612 | // remove the node only if it's unknown or not live 613 | if (!map || !map.has(a[aStart])) 614 | parentNode.removeChild(get(a[aStart], -1)); 615 | aStart++; 616 | } 617 | } 618 | // same node: fast path 619 | else if (a[aStart] === b[bStart]) { 620 | aStart++; 621 | bStart++; 622 | } 623 | // same tail: fast path 624 | else if (a[aEnd - 1] === b[bEnd - 1]) { 625 | aEnd--; 626 | bEnd--; 627 | } 628 | // The once here single last swap "fast path" has been removed in v1.1.0 629 | // https://github.com/WebReflection/udomdiff/blob/single-final-swap/esm/index.js#L69-L85 630 | // reverse swap: also fast path 631 | else if ( 632 | a[aStart] === b[bEnd - 1] && 633 | b[bStart] === a[aEnd - 1] 634 | ) { 635 | // this is a "shrink" operation that could happen in these cases: 636 | // [1, 2, 3, 4, 5] 637 | // [1, 4, 3, 2, 5] 638 | // or asymmetric too 639 | // [1, 2, 3, 4, 5] 640 | // [1, 2, 3, 5, 6, 4] 641 | const node = get(a[--aEnd], -1).nextSibling; 642 | parentNode.insertBefore( 643 | get(b[bStart++], 1), 644 | get(a[aStart++], -1).nextSibling 645 | ); 646 | parentNode.insertBefore(get(b[--bEnd], 1), node); 647 | // mark the future index as identical (yeah, it's dirty, but cheap 👍) 648 | // The main reason to do this, is that when a[aEnd] will be reached, 649 | // the loop will likely be on the fast path, as identical to b[bEnd]. 650 | // In the best case scenario, the next loop will skip the tail, 651 | // but in the worst one, this node will be considered as already 652 | // processed, bailing out pretty quickly from the map index check 653 | a[aEnd] = b[bEnd]; 654 | } 655 | // map based fallback, "slow" path 656 | else { 657 | // the map requires an O(bEnd - bStart) operation once 658 | // to store all future nodes indexes for later purposes. 659 | // In the worst case scenario, this is a full O(N) cost, 660 | // and such scenario happens at least when all nodes are different, 661 | // but also if both first and last items of the lists are different 662 | if (!map) { 663 | map = new Map; 664 | let i = bStart; 665 | while (i < bEnd) 666 | map.set(b[i], i++); 667 | } 668 | // if it's a future node, hence it needs some handling 669 | if (map.has(a[aStart])) { 670 | // grab the index of such node, 'cause it might have been processed 671 | const index = map.get(a[aStart]); 672 | // if it's not already processed, look on demand for the next LCS 673 | if (bStart < index && index < bEnd) { 674 | let i = aStart; 675 | // counts the amount of nodes that are the same in the future 676 | let sequence = 1; 677 | while (++i < aEnd && i < bEnd && map.get(a[i]) === (index + sequence)) 678 | sequence++; 679 | // effort decision here: if the sequence is longer than replaces 680 | // needed to reach such sequence, which would brings again this loop 681 | // to the fast path, prepend the difference before a sequence, 682 | // and move only the future list index forward, so that aStart 683 | // and bStart will be aligned again, hence on the fast path. 684 | // An example considering aStart and bStart are both 0: 685 | // a: [1, 2, 3, 4] 686 | // b: [7, 1, 2, 3, 6] 687 | // this would place 7 before 1 and, from that time on, 1, 2, and 3 688 | // will be processed at zero cost 689 | if (sequence > (index - bStart)) { 690 | const node = get(a[aStart], 0); 691 | while (bStart < index) 692 | parentNode.insertBefore(get(b[bStart++], 1), node); 693 | } 694 | // if the effort wasn't good enough, fallback to a replace, 695 | // moving both source and target indexes forward, hoping that some 696 | // similar node will be found later on, to go back to the fast path 697 | else { 698 | parentNode.replaceChild( 699 | get(b[bStart++], 1), 700 | get(a[aStart++], -1) 701 | ); 702 | } 703 | } 704 | // otherwise move the source forward, 'cause there's nothing to do 705 | else 706 | aStart++; 707 | } 708 | // this node has no meaning in the future list, so it's more than safe 709 | // to remove it, and check the next live node out instead, meaning 710 | // that only the live list index should be forwarded 711 | else 712 | parentNode.removeChild(get(a[aStart++], -1)); 713 | } 714 | } 715 | return b; 716 | }; 717 | 718 | const {isArray, prototype} = Array; 719 | const {indexOf} = prototype; 720 | 721 | const { 722 | createDocumentFragment, 723 | createElement, 724 | createElementNS, 725 | createTextNode, 726 | createTreeWalker, 727 | importNode 728 | } = new Proxy(document, { 729 | get: (target, method) => target[method].bind(target) 730 | }); 731 | 732 | const createHTML = html => { 733 | const template = createElement('template'); 734 | template.innerHTML = html; 735 | return template.content; 736 | }; 737 | 738 | let xml; 739 | const createSVG = svg => { 740 | if (!xml) xml = createElementNS('http://www.w3.org/2000/svg', 'svg'); 741 | xml.innerHTML = svg; 742 | const content = createDocumentFragment(); 743 | content.append(...xml.childNodes); 744 | return content; 745 | }; 746 | 747 | const createContent = (text, svg) => svg ? 748 | createSVG(text) : createHTML(text); 749 | 750 | // from a generic path, retrieves the exact targeted node 751 | const reducePath = ({childNodes}, i) => childNodes[i]; 752 | 753 | // this helper avoid code bloat around handleAnything() callback 754 | const diff = (comment, oldNodes, newNodes) => udomdiff( 755 | comment.parentNode, 756 | // TODO: there is a possible edge case where a node has been 757 | // removed manually, or it was a keyed one, attached 758 | // to a shared reference between renders. 759 | // In this case udomdiff might fail at removing such node 760 | // as its parent won't be the expected one. 761 | // The best way to avoid this issue is to filter oldNodes 762 | // in search of those not live, or not in the current parent 763 | // anymore, but this would require both a change to uwire, 764 | // exposing a parentNode from the firstChild, as example, 765 | // but also a filter per each diff that should exclude nodes 766 | // that are not in there, penalizing performance quite a lot. 767 | // As this has been also a potential issue with domdiff, 768 | // and both lighterhtml and hyperHTML might fail with this 769 | // very specific edge case, I might as well document this possible 770 | // "diffing shenanigan" and call it a day. 771 | oldNodes, 772 | newNodes, 773 | diffable, 774 | comment 775 | ); 776 | 777 | // if an interpolation represents a comment, the whole 778 | // diffing will be related to such comment. 779 | // This helper is in charge of understanding how the new 780 | // content for such interpolation/hole should be updated 781 | const handleAnything = comment => { 782 | let oldValue, text, nodes = []; 783 | const anyContent = newValue => { 784 | switch (typeof newValue) { 785 | // primitives are handled as text content 786 | case 'string': 787 | case 'number': 788 | case 'boolean': 789 | if (oldValue !== newValue) { 790 | oldValue = newValue; 791 | if (!text) 792 | text = createTextNode(''); 793 | text.data = newValue; 794 | nodes = diff(comment, nodes, [text]); 795 | } 796 | break; 797 | // null, and undefined are used to cleanup previous content 798 | case 'object': 799 | case 'undefined': 800 | if (newValue == null) { 801 | if (oldValue != newValue) { 802 | oldValue = newValue; 803 | nodes = diff(comment, nodes, []); 804 | } 805 | break; 806 | } 807 | // arrays and nodes have a special treatment 808 | if (isArray(newValue)) { 809 | oldValue = newValue; 810 | // arrays can be used to cleanup, if empty 811 | if (newValue.length === 0) 812 | nodes = diff(comment, nodes, []); 813 | // or diffed, if these contains nodes or "wires" 814 | else if (typeof newValue[0] === 'object') 815 | nodes = diff(comment, nodes, newValue); 816 | // in all other cases the content is stringified as is 817 | else 818 | anyContent(String(newValue)); 819 | break; 820 | } 821 | // if the new value is a DOM node, or a wire, and it's 822 | // different from the one already live, then it's diffed. 823 | // if the node is a fragment, it's appended once via its childNodes 824 | // There is no `else` here, meaning if the content 825 | // is not expected one, nothing happens, as easy as that. 826 | if (oldValue !== newValue && 'ELEMENT_NODE' in newValue) { 827 | oldValue = newValue; 828 | nodes = diff( 829 | comment, 830 | nodes, 831 | newValue.nodeType === 11 ? 832 | [...newValue.childNodes] : 833 | [newValue] 834 | ); 835 | } 836 | break; 837 | case 'function': 838 | anyContent(newValue(comment)); 839 | break; 840 | } 841 | }; 842 | return anyContent; 843 | }; 844 | 845 | // attributes can be: 846 | // * ref=${...} for hooks and other purposes 847 | // * aria=${...} for aria attributes 848 | // * ?boolean=${...} for boolean attributes 849 | // * .dataset=${...} for dataset related attributes 850 | // * .setter=${...} for Custom Elements setters or nodes with setters 851 | // such as buttons, details, options, select, etc 852 | // * @event=${...} to explicitly handle event listeners 853 | // * onevent=${...} to automatically handle event listeners 854 | // * generic=${...} to handle an attribute just like an attribute 855 | const handleAttribute = (node, name/*, svg*/) => { 856 | switch (name[0]) { 857 | case '?': return boolean(node, name.slice(1), false); 858 | case '.': return setter(node, name.slice(1)); 859 | case '@': return event(node, 'on' + name.slice(1)); 860 | case 'o': if (name[1] === 'n') return event(node, name); 861 | } 862 | 863 | switch (name) { 864 | case 'ref': return ref(node); 865 | case 'aria': return aria(node); 866 | } 867 | 868 | return attribute(node, name/*, svg*/); 869 | }; 870 | 871 | // each mapped update carries the update type and its path 872 | // the type is either node, attribute, or text, while 873 | // the path is how to retrieve the related node to update. 874 | // In the attribute case, the attribute name is also carried along. 875 | function handlers(options) { 876 | const {type, path} = options; 877 | const node = path.reduceRight(reducePath, this); 878 | return type === 'node' ? 879 | handleAnything(node) : 880 | (type === 'attr' ? 881 | handleAttribute(node, options.name/*, options.svg*/) : 882 | text(node)); 883 | } 884 | 885 | // from a fragment container, create an array of indexes 886 | // related to its child nodes, so that it's possible 887 | // to retrieve later on exact node via reducePath 888 | const createPath = node => { 889 | const path = []; 890 | let {parentNode} = node; 891 | while (parentNode) { 892 | path.push(indexOf.call(parentNode.childNodes, node)); 893 | node = parentNode; 894 | ({parentNode} = node); 895 | } 896 | return path; 897 | }; 898 | 899 | // the prefix is used to identify either comments, attributes, or nodes 900 | // that contain the related unique id. In the attribute cases 901 | // isµX="attribute-name" will be used to map current X update to that 902 | // attribute name, while comments will be like , to map 903 | // the update to that specific comment node, hence its parent. 904 | // style and textarea will have text content, and are handled 905 | // directly through text-only updates. 906 | const prefix = 'isµ'; 907 | 908 | // Template Literals are unique per scope and static, meaning a template 909 | // should be parsed once, and once only, as it will always represent the same 910 | // content, within the exact same amount of updates each time. 911 | // This cache relates each template to its unique content and updates. 912 | const cache$2 = new WeakMapSet; 913 | 914 | // a RegExp that helps checking nodes that cannot contain comments 915 | const textOnly = /^(?:textarea|script|style|title|plaintext|xmp)$/; 916 | 917 | const createCache$1 = () => ({ 918 | stack: [], // each template gets a stack for each interpolation "hole" 919 | 920 | entry: null, // each entry contains details, such as: 921 | // * the template that is representing 922 | // * the type of node it represents (html or svg) 923 | // * the content fragment with all nodes 924 | // * the list of updates per each node (template holes) 925 | // * the "wired" node or fragment that will get updates 926 | // if the template or type are different from the previous one 927 | // the entry gets re-created each time 928 | 929 | wire: null // each rendered node represent some wired content and 930 | // this reference to the latest one. If different, the node 931 | // will be cleaned up and the new "wire" will be appended 932 | }); 933 | 934 | // the entry stored in the rendered node cache, and per each "hole" 935 | const createEntry = (type, template) => { 936 | const {content, updates} = mapUpdates(type, template); 937 | return {type, template, content, updates, wire: null}; 938 | }; 939 | 940 | // a template is instrumented to be able to retrieve where updates are needed. 941 | // Each unique template becomes a fragment, cloned once per each other 942 | // operation based on the same template, i.e. data => html`

${data}

` 943 | const mapTemplate = (type, template) => { 944 | const svg = type === 'svg'; 945 | const text = instrument(template, prefix, svg); 946 | const content = createContent(text, svg); 947 | // once instrumented and reproduced as fragment, it's crawled 948 | // to find out where each update is in the fragment tree 949 | const tw = createTreeWalker(content, 1 | 128); 950 | const nodes = []; 951 | const length = template.length - 1; 952 | let i = 0; 953 | // updates are searched via unique names, linearly increased across the tree 954 | //
955 | let search = `${prefix}${i}`; 956 | while (i < length) { 957 | const node = tw.nextNode(); 958 | // if not all updates are bound but there's nothing else to crawl 959 | // it means that there is something wrong with the template. 960 | if (!node) 961 | throw `bad template: ${text}`; 962 | // if the current node is a comment, and it contains isµX 963 | // it means the update should take care of any content 964 | if (node.nodeType === 8) { 965 | // The only comments to be considered are those 966 | // which content is exactly the same as the searched one. 967 | if (node.data === search) { 968 | nodes.push({type: 'node', path: createPath(node)}); 969 | search = `${prefix}${++i}`; 970 | } 971 | } 972 | else { 973 | // if the node is not a comment, loop through all its attributes 974 | // named isµX and relate attribute updates to this node and the 975 | // attribute name, retrieved through node.getAttribute("isµX") 976 | // the isµX attribute will be removed as irrelevant for the layout 977 | // let svg = -1; 978 | while (node.hasAttribute(search)) { 979 | nodes.push({ 980 | type: 'attr', 981 | path: createPath(node), 982 | name: node.getAttribute(search) 983 | }); 984 | node.removeAttribute(search); 985 | search = `${prefix}${++i}`; 986 | } 987 | // if the node was a style, textarea, or others, check its content 988 | // and if it is then update tex-only this node 989 | if ( 990 | textOnly.test(node.localName) && 991 | node.textContent.trim() === `` 992 | ){ 993 | node.textContent = ''; 994 | nodes.push({type: 'text', path: createPath(node)}); 995 | search = `${prefix}${++i}`; 996 | } 997 | } 998 | } 999 | // once all nodes to update, or their attributes, are known, the content 1000 | // will be cloned in the future to represent the template, and all updates 1001 | // related to such content retrieved right away without needing to re-crawl 1002 | // the exact same template, and its content, more than once. 1003 | return {content, nodes}; 1004 | }; 1005 | 1006 | // if a template is unknown, perform the previous mapping, otherwise grab 1007 | // its details such as the fragment with all nodes, and updates info. 1008 | const mapUpdates = (type, template) => { 1009 | const {content, nodes} = ( 1010 | cache$2.get(template) || 1011 | cache$2.set(template, mapTemplate(type, template)) 1012 | ); 1013 | // clone deeply the fragment 1014 | const fragment = importNode(content, true); 1015 | // and relate an update handler per each node that needs one 1016 | const updates = nodes.map(handlers, fragment); 1017 | // return the fragment and all updates to use within its nodes 1018 | return {content: fragment, updates}; 1019 | }; 1020 | 1021 | // as html and svg can be nested calls, but no parent node is known 1022 | // until rendered somewhere, the unroll operation is needed to 1023 | // discover what to do with each interpolation, which will result 1024 | // into an update operation. 1025 | const unroll$1 = (info, {type, template, values}) => { 1026 | // interpolations can contain holes and arrays, so these need 1027 | // to be recursively discovered 1028 | const length = unrollValues$1(info, values); 1029 | let {entry} = info; 1030 | // if the cache entry is either null or different from the template 1031 | // and the type this unroll should resolve, create a new entry 1032 | // assigning a new content fragment and the list of updates. 1033 | if (!entry || (entry.template !== template || entry.type !== type)) 1034 | info.entry = (entry = createEntry(type, template)); 1035 | const {content, updates, wire} = entry; 1036 | // even if the fragment and its nodes is not live yet, 1037 | // it is already possible to update via interpolations values. 1038 | for (let i = 0; i < length; i++) 1039 | updates[i](values[i]); 1040 | // if the entry was new, or representing a different template or type, 1041 | // create a new persistent entity to use during diffing. 1042 | // This is simply a DOM node, when the template has a single container, 1043 | // as in `

`, or a "wire" in `

` and similar cases. 1044 | return wire || (entry.wire = persistent(content)); 1045 | }; 1046 | 1047 | // the stack retains, per each interpolation value, the cache 1048 | // related to each interpolation value, or null, if the render 1049 | // was conditional and the value is not special (Array or Hole) 1050 | const unrollValues$1 = ({stack}, values) => { 1051 | const {length} = values; 1052 | for (let i = 0; i < length; i++) { 1053 | const hole = values[i]; 1054 | // each Hole gets unrolled and re-assigned as value 1055 | // so that domdiff will deal with a node/wire, not with a hole 1056 | if (hole instanceof Hole) 1057 | values[i] = unroll$1( 1058 | stack[i] || (stack[i] = createCache$1()), 1059 | hole 1060 | ); 1061 | // arrays are recursively resolved so that each entry will contain 1062 | // also a DOM node or a wire, hence it can be diffed if/when needed 1063 | else if (isArray(hole)) 1064 | unrollValues$1(stack[i] || (stack[i] = createCache$1()), hole); 1065 | // if the value is nothing special, the stack doesn't need to retain data 1066 | // this is useful also to cleanup previously retained data, if the value 1067 | // was a Hole, or an Array, but not anymore, i.e.: 1068 | // const update = content => html`
${content}
`; 1069 | // update(listOfItems); update(null); update(html`hole`) 1070 | else 1071 | stack[i] = null; 1072 | } 1073 | if (length < stack.length) 1074 | stack.splice(length); 1075 | return length; 1076 | }; 1077 | 1078 | /** 1079 | * Holds all details wrappers needed to render the content further on. 1080 | * @constructor 1081 | * @param {string} type The hole type, either `html` or `svg`. 1082 | * @param {string[]} template The template literals used to the define the content. 1083 | * @param {Array} values Zero, one, or more interpolated values to render. 1084 | */ 1085 | class Hole { 1086 | constructor(type, template, values) { 1087 | this.type = type; 1088 | this.template = template; 1089 | this.values = values; 1090 | } 1091 | } 1092 | 1093 | // both `html` and `svg` template literal tags are polluted 1094 | // with a `for(ref[, id])` and a `node` tag too 1095 | const tag = type => { 1096 | // both `html` and `svg` tags have their own cache 1097 | const keyed = new WeakMapSet; 1098 | // keyed operations always re-use the same cache and unroll 1099 | // the template and its interpolations right away 1100 | const fixed = cache => (template, ...values) => unroll$1( 1101 | cache, 1102 | {type, template, values} 1103 | ); 1104 | return Object.assign( 1105 | // non keyed operations are recognized as instance of Hole 1106 | // during the "unroll", recursively resolved and updated 1107 | (template, ...values) => new Hole(type, template, values), 1108 | { 1109 | // keyed operations need a reference object, usually the parent node 1110 | // which is showing keyed results, and optionally a unique id per each 1111 | // related node, handy with JSON results and mutable list of objects 1112 | // that usually carry a unique identifier 1113 | for(ref, id) { 1114 | const memo = keyed.get(ref) || keyed.set(ref, new MapSet); 1115 | return memo.get(id) || memo.set(id, fixed(createCache$1())); 1116 | }, 1117 | // it is possible to create one-off content out of the box via node tag 1118 | // this might return the single created node, or a fragment with all 1119 | // nodes present at the root level and, of course, their child nodes 1120 | node: (template, ...values) => unroll$1(createCache$1(), new Hole(type, template, values)).valueOf() 1121 | } 1122 | ); 1123 | }; 1124 | 1125 | // each rendered node gets its own cache 1126 | const cache$1 = new WeakMapSet; 1127 | 1128 | // rendering means understanding what `html` or `svg` tags returned 1129 | // and it relates a specific node to its own unique cache. 1130 | // Each time the content to render changes, the node is cleaned up 1131 | // and the new new content is appended, and if such content is a Hole 1132 | // then it's "unrolled" to resolve all its inner nodes. 1133 | const render$1 = (where, what) => { 1134 | const hole = typeof what === 'function' ? what() : what; 1135 | const info = cache$1.get(where) || cache$1.set(where, createCache$1()); 1136 | const wire = hole instanceof Hole ? unroll$1(info, hole) : hole; 1137 | if (wire !== info.wire) { 1138 | info.wire = wire; 1139 | // valueOf() simply returns the node itself, but in case it was a "wire" 1140 | // it will eventually re-append all nodes to its fragment so that such 1141 | // fragment can be re-appended many times in a meaningful way 1142 | // (wires are basically persistent fragments facades with special behavior) 1143 | where.replaceChildren(wire.valueOf()); 1144 | } 1145 | return where; 1146 | }; 1147 | 1148 | const html$1 = tag('html'); 1149 | const svg$1 = tag('svg'); 1150 | 1151 | const {create} = Object; 1152 | 1153 | const html = (template, ...values) => new Hole('html', template, values); 1154 | html.for = createFor(html$1); 1155 | 1156 | const svg = (template, ...values) => new Hole('svg', template, values); 1157 | svg.for = createFor(svg$1); 1158 | 1159 | const cache = umap(new WeakMap); 1160 | 1161 | const render = (where, what) => ( 1162 | cache.get(where) || cache.set(where, { 1163 | c: createCache(), 1164 | h: hooked( 1165 | /*async*/ function (what) { 1166 | const value = /*await*/ (typeof what === 'function' ? what() : what); 1167 | return render$1( 1168 | where, 1169 | value instanceof Hook ? 1170 | /*await*/ unroll(this.c, value) : 1171 | (/*await*/ unrollHole(this.c, value), value) 1172 | ); 1173 | }, 1174 | where 1175 | ) 1176 | }) 1177 | ).h(what); 1178 | 1179 | const createHook = (info, entry) => hooked(/*async*/ function () { 1180 | const hole = /*await*/ entry.f.apply(this, arguments); 1181 | if (hole instanceof Hole) { 1182 | /*await*/ unrollHole(info, hole); 1183 | entry.$ = view(entry, hole); 1184 | } 1185 | else 1186 | entry.$ = hole; 1187 | return entry.$; 1188 | }); 1189 | 1190 | const createCache = () => ({s: [], e: null}); 1191 | 1192 | const unroll = (info, {f, c, a}) => { 1193 | let {e} = info; 1194 | if (!e || e.f !== f) { 1195 | info.e = (e = {f, h: null, $: null}); 1196 | e.h = createHook(createCache(), e); 1197 | } 1198 | return e.h.apply(c, a); 1199 | }; 1200 | 1201 | const unrollHole = /*async*/ (info, {values}) => { 1202 | /*await*/ unrollValues(info, values); 1203 | }; 1204 | 1205 | const unrollValues = /*async*/ (info, values) => { 1206 | const {s} = info, {length} = values; 1207 | for (let i = 0; i < length; i++) { 1208 | const hook = /*await*/ values[i]; 1209 | if (hook instanceof Hook) 1210 | values[i] = /*await*/ unroll(s[i] || (s[i] = createCache()), hook); 1211 | else if (hook instanceof Hole) 1212 | /*await*/ unrollHole(s[i] || (s[i] = createCache()), hook); 1213 | else if (isArray$1(hook)) 1214 | /*await*/ unrollValues(s[i] || (s[i] = createCache()), hook); 1215 | else 1216 | s[i] = null; 1217 | } 1218 | if (length < s.length) 1219 | s.splice(length); 1220 | }; 1221 | 1222 | const view = (e, {type, template, values}) => 1223 | (type === 'svg' ? svg$1 : html$1) 1224 | .for(e, type)(template, ...values); 1225 | 1226 | function Component(f) { 1227 | return function () { 1228 | return new Hook(f, this, arguments); 1229 | }; 1230 | } 1231 | 1232 | function Hook(f, c, a) { 1233 | this.f = f; 1234 | this.c = c; 1235 | this.a = a; 1236 | } 1237 | 1238 | function createFor(uhtml) { 1239 | const cache = umap(new WeakMap); 1240 | return ( 1241 | (e, id) => { 1242 | const store = cache.get(e) || cache.set(e, create(null)); 1243 | const info = store[id] || (store[id] = createCache()); 1244 | return /*async*/ (template, ...values) => { 1245 | /*await*/ unrollValues(info, values); 1246 | return uhtml.for(e, id)(template, ...values); 1247 | }; 1248 | } 1249 | ); 1250 | } 1251 | 1252 | exports.Component = Component; 1253 | exports.createContext = createContext; 1254 | exports.html = html; 1255 | exports.render = render; 1256 | exports.svg = svg; 1257 | exports.useCallback = useCallback; 1258 | exports.useContext = useContext; 1259 | exports.useEffect = useEffect; 1260 | exports.useLayoutEffect = useLayoutEffect; 1261 | exports.useMemo = useMemo; 1262 | exports.useReducer = useReducer; 1263 | exports.useRef = useRef; 1264 | exports.useState = useState; 1265 | 1266 | return exports; 1267 | 1268 | })({}); 1269 | -------------------------------------------------------------------------------- /min.js: -------------------------------------------------------------------------------- 1 | self.uland=function(e){"use strict";var t=CustomEvent;function n(e){this.observe(e,{subtree:!0,childList:!0})}function r(e){e.type in this&&this[e.type](e)}var s=Promise;let o=null,l=new Set;const c=e=>{const{$:t,r:n,h:r}=e;m(n)&&(i.get(r).delete(e),n()),m(e.r=t())&&i.get(r).add(e)},a=()=>{const e=l;l=new Set,e.forEach((({h:e,c:t,a:n,e:r})=>{r&&e.apply(t,n)}))},i=new WeakMap,u=[],d=[];function h(e,t){return e!==this[t]}const f=e=>{const t=i.get(e);t&&w.then((()=>{t.forEach((e=>{e.r(),e.r=null,e.d=!0})),t.clear()}))},p=()=>o,g=e=>i.has(e),m=e=>"function"==typeof e,y=e=>{const t={h:n,c:null,a:null,e:0,i:0,s:[]};return n;function n(){const n=o;o=t,t.e=t.i=0;try{return e.apply(t.c=this,t.a=arguments)}finally{o=n,u.length&&w.then(u.forEach.bind(u.splice(0),c)),d.length&&d.splice(0).forEach(c)}}},w=new s((e=>e()));function v(e){const{_:t,value:n}=this;n!==e&&(this._=new Set,this.value=e,t.forEach((({h:e,c:t,a:n})=>{e.apply(t,n)})))}const b=(e,t)=>{const n=p(),{i:r,s:s}=n;return r!==s.length&&t&&!t.some(h,s[r]._)||(s[r]={$:e(),_:t}),s[n.i++].$},E=e=>(t,n)=>{const r=p(),{i:s,s:o,h:l}=r,c=s===o.length;r.i++,c&&(i.has(l)||i.set(l,new Set),o[s]={$:t,_:n,r:null,d:!1,h:l}),(c||!n||o[s].d||n.some(h,o[s]._))&&e.push(o[s]),o[s].$=t,o[s]._=n},x=E(u),k=E(d),C=(e,t)=>m(t)?t(e):t,N=(e,t,n)=>{const r=p(),{i:s,s:o}=r;s===o.length&&o.push({$:m(n)?n(t):C(void 0,t),set:t=>{o[s].$=e(o[s].$,t),(e=>{l.has(e)||(e.e=1,l.add(e),w.then(a))})(r)}});const{$:c,set:i}=o[r.i++];return[c,i]}; 2 | /*! (c) Andrea Giammarchi - ISC */ 3 | let $=null,A=null,M=null;const S=new WeakMap,T=new WeakMap,_=(e,t,n,r)=>{const s=s=>{S.has(e)||(S.set(e,0),w.then((()=>{S.delete(e),e.apply(t,n)}))),r(s)};return T.set(r,s),s},L=(e,t,n,r)=>e?[r[0],T.get(r[1])||_(e,t,n,r[1])]:r,O=(e,t)=>{const n=y(t?function(){const[t,r,s]=[$,A,M];[$,A,M]=[n,this,arguments];try{return e.apply(A,M)}finally{[$,A,M]=[t,r,s]}}:e);return n},W=((e,t,s,o)=>{const l=new WeakMap,c=new WeakMap,a=new WeakMap,i=e=>l.has(e),u=e=>{i(e)&&(d(e,e.removeEventListener,l.get(e)),l.delete(e))},d=(e,t,n)=>{t.call(e,"disconnected",n),t.call(e,"connected",n)},h=(e,t,n,r)=>{for(let{length:s}=e,o=0;o{i(e)&&!r.has(e)&&(o.delete(e),r.set(e,0),e.dispatchEvent(new(s||CustomEvent)(n))),h(e[t||"children"]||[],n,r,o)},p=new(o||MutationObserver)((e=>{for(let{length:t}=e,n=0;n{u(e),(t||(t={})).handleEvent||(t.handleEvent=r),d(e,e.addEventListener,t),l.set(e,t)},disconnect:u,kill(){p.disconnect()}}})(document,"children",t),j=({firstChild:e})=>{if(e&&1!==e.nodeType&&!(e=e.nextElementSibling))throw"unobservable";return e},B=e=>{const{nodeType:t}=e;if(t)return 1===t?e:j(e);{const t=e.valueOf();return t!==e?B(t):j(t)}},R=(e,t)=>{const n=O(e,t);return function(){const e=n.apply(this,arguments);if(g(n)){const t=B(e);W.has(t)||W.connect(t,{disconnected(){f(n)}})}return e}};var D=e=>({get:t=>e.get(t),set:(t,n)=>(e.set(t,n),n)});const{isArray:z}=Array;class H extends Map{set(e,t){return super.set(e,t),t}}class P extends WeakMap{set(e,t){return super.set(e,t),t}} 4 | /*! (c) Andrea Giammarchi - ISC */const F=/^(?:area|base|br|col|embed|hr|img|input|keygen|link|menuitem|meta|param|source|track|wbr)$/i,q=/<([a-z]+[a-z0-9:._-]*)([^>]*?)(\/?)>/g,G=/([^\s\\>"'=]+)\s*=\s*(['"]?)\x01/g,I=/[\x01\x02]/g;const J=(e,t)=>111===e.nodeType?1/t<0?t?(({firstChild:e,lastChild:t})=>{const n=document.createRange();return n.setStartAfter(e),n.setEndAfter(t),n.deleteContents(),e})(e):e.lastChild:t?e.valueOf():e.firstChild:e,K=(e,t)=>{let n,r,s=t.slice(2);return!(t in e)&&(r=t.toLowerCase())in e&&(s=r.slice(2)),t=>{const r=z(t)?t:[t,!1];n!==r[0]&&(n&&e.removeEventListener(s,n,r[1]),(n=r[0])&&e.addEventListener(s,n,r[1]))}};const{isArray:Q,prototype:U}=Array,{indexOf:V}=U,{createDocumentFragment:X,createElement:Y,createElementNS:Z,createTextNode:ee,createTreeWalker:te,importNode:ne}=new Proxy(document,{get:(e,t)=>e[t].bind(e)});let re;const se=(e,t)=>t?(e=>{re||(re=Z("http://www.w3.org/2000/svg","svg")),re.innerHTML=e;const t=X();return t.append(...re.childNodes),t})(e):(e=>{const t=Y("template");return t.innerHTML=e,t.content})(e),oe=({childNodes:e},t)=>e[t],le=(e,t,n)=>((e,t,n,r,s)=>{const o=n.length;let l=t.length,c=o,a=0,i=0,u=null;for(;as-i){const o=r(t[a],0);for(;i{switch(t[0]){case"?":return((e,t,n)=>r=>{n!==!!r&&((n=!!r)?e.setAttribute(t,""):e.removeAttribute(t))})(e,t.slice(1),!1);case".":return((e,t)=>"dataset"===t?(({dataset:e})=>t=>{for(const n in t){const r=t[n];null==r?delete e[n]:e[n]=r}})(e):n=>{e[t]=n})(e,t.slice(1));case"@":return K(e,"on"+t.slice(1));case"o":if("n"===t[1])return K(e,t)}switch(t){case"ref":return(e=>{let t;return n=>{t!==n&&(t=n,"function"==typeof n?n(e):n.current=e)}})(e);case"aria":return(e=>t=>{for(const n in t){const r="role"===n?n:`aria-${n}`,s=t[n];null==s?e.removeAttribute(r):e.setAttribute(r,s)}})(e)}return((e,t)=>{let n,r=!0;const s=document.createAttributeNS(null,t);return t=>{if(n!==t)if(n=t,null==n)r||(e.removeAttributeNode(s),r=!0);else{const n=t;null==n?(r||e.removeAttributeNode(s),r=!0):(s.value=n,r&&(e.setAttributeNodeNS(s),r=!1))}}})(e,t)};function ae(e){const{type:t,path:n}=e,r=n.reduceRight(oe,this);return"node"===t?(e=>{let t,n,r=[];const s=o=>{switch(typeof o){case"string":case"number":case"boolean":t!==o&&(t=o,n||(n=ee("")),n.data=o,r=le(e,r,[n]));break;case"object":case"undefined":if(null==o){t!=o&&(t=o,r=le(e,r,[]));break}if(Q(o)){t=o,0===o.length?r=le(e,r,[]):"object"==typeof o[0]?r=le(e,r,o):s(String(o));break}t!==o&&"ELEMENT_NODE"in o&&(t=o,r=le(e,r,11===o.nodeType?[...o.childNodes]:[o]));break;case"function":s(o(e))}};return s})(r):"attr"===t?ce(r,e.name):(e=>{let t;return n=>{t!=n&&(t=n,e.textContent=null==n?"":n)}})(r)}const ie=e=>{const t=[];let{parentNode:n}=e;for(;n;)t.push(V.call(n.childNodes,e)),e=n,({parentNode:n}=e);return t},ue="isµ",de=new P,he=/^(?:textarea|script|style|title|plaintext|xmp)$/,fe=(e,t)=>{const n="svg"===e,r=((e,t,n)=>{let r=0;return e.join("").trim().replace(q,((e,t,r,s)=>{let o=t+r.replace(G,"=$2$1").trimEnd();return s.length&&(o+=n||F.test(t)?" /":">"})).replace(I,(e=>""===e?"\x3c!--"+t+r+++"--\x3e":t+r++))})(t,ue,n),s=se(r,n),o=te(s,129),l=[],c=t.length-1;let a=0,i=`isµ${a}`;for(;a{const{content:n,nodes:r}=de.get(t)||de.set(t,fe(e,t)),s=ne(n,!0);return{content:s,updates:r.map(ae,s)}},ge=(e,{type:t,template:n,values:r})=>{const s=me(e,r);let{entry:o}=e;o&&o.template===n&&o.type===t||(e.entry=o=((e,t)=>{const{content:n,updates:r}=pe(e,t);return{type:e,template:t,content:n,updates:r,wire:null}})(t,n));const{content:l,updates:c,wire:a}=o;for(let e=0;e{const{firstChild:t,lastChild:n}=e;if(t===n)return n||e;const{childNodes:r}=e,s=[...r];return{ELEMENT_NODE:1,nodeType:111,firstChild:t,lastChild:n,valueOf:()=>(r.length!==s.length&&e.append(...s),e)}})(l))},me=({stack:e},t)=>{const{length:n}=t;for(let r=0;r{const t=new P;return Object.assign(((t,...n)=>new ye(e,t,n)),{for(n,r){const s=t.get(n)||t.set(n,new H);return s.get(r)||s.set(r,(t=>(n,...r)=>ge(t,{type:e,template:n,values:r}))({stack:[],entry:null,wire:null}))},node:(t,...n)=>ge({stack:[],entry:null,wire:null},new ye(e,t,n)).valueOf()})},ve=new P,be=we("html"),Ee=we("svg"),{create:xe}=Object,ke=(e,...t)=>new ye("html",e,t);ke.for=Oe(be);const Ce=(e,...t)=>new ye("svg",e,t);Ce.for=Oe(Ee);const Ne=D(new WeakMap),$e=(e,t)=>R((function(){const n=t.f.apply(this,arguments);return n instanceof ye?(Se(e,n),t.$=_e(t,n)):t.$=n,t.$})),Ae=()=>({s:[],e:null}),Me=(e,{f:t,c:n,a:r})=>{let{e:s}=e;return s&&s.f===t||(e.e=s={f:t,h:null,$:null},s.h=$e(Ae(),s)),s.h.apply(n,r)},Se=(e,{values:t})=>{Te(e,t)},Te=(e,t)=>{const{s:n}=e,{length:r}=t;for(let e=0;e("svg"===t?Ee:be).for(e,t)(n,...r);function Le(e,t,n){this.f=e,this.c=t,this.a=n}function Oe(e){const t=D(new WeakMap);return(n,r)=>{const s=t.get(n)||t.set(n,xe(null)),o=s[r]||(s[r]=Ae());return(t,...s)=>(Te(o,s),e.for(n,r)(t,...s))}}return e.Component=function(e){return function(){return new Le(e,this,arguments)}},e.createContext=e=>({_:new Set,provide:v,value:e}),e.html=ke,e.render=(e,t)=>(Ne.get(e)||Ne.set(e,{c:Ae(),h:R((function(t){const n="function"==typeof t?t():t;return((e,t)=>{const n="function"==typeof t?t():t,r=ve.get(e)||ve.set(e,{stack:[],entry:null,wire:null}),s=n instanceof ye?ge(r,n):n;return s!==r.wire&&(r.wire=s,e.replaceChildren(s.valueOf())),e})(e,n instanceof Le?Me(this.c,n):(Se(this.c,n),n))}),e)})).h(t),e.svg=Ce,e.useCallback=(e,t)=>b((()=>e),t),e.useContext=({_:e,value:t})=>(e.add(p()),t),e.useEffect=x,e.useLayoutEffect=k,e.useMemo=b,e.useReducer=(e,t,n)=>L($,A,M,N(e,t,n)),e.useRef=e=>{const t=p(),{i:n,s:r}=t;return n===r.length&&r.push({current:e}),r[t.i++]},e.useState=e=>L($,A,M,(e=>N(C,e))(e)) 5 | /*! (c) Andrea Giammarchi - ISC */,e}({}); -------------------------------------------------------------------------------- /min.js.br: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebReflection/uland/2e896aecbf30c945ad0a9503094536f76aa752fa/min.js.br -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "uland", 3 | "version": "1.0.1", 4 | "main": "./cjs/index.js", 5 | "types": "index.d.ts", 6 | "scripts": { 7 | "build": "npm run async && npm run cjs && npm run rollup:async && npm run rollup:es && npm run rollup:index && npm run rollup:esm && npm run size", 8 | "cjs": "ascjs --no-default esm cjs", 9 | "async": "cp esm/index.js esm/async.js && sed -i.bck 's/uhooks-dom/uhooks-dom\\/async/; s/\\/\\*async\\*\\//async/; s/\\/\\*await\\*\\//await/' esm/async.js && rm -rf esm/async.js.bck", 10 | "rollup:async": "rollup --config rollup/async.config.js && sed -i.bck 's/^var /self./' async.js && rm -rf async.js.bck", 11 | "rollup:esm": "rollup --config rollup/esm.config.js", 12 | "rollup:es": "rollup --config rollup/es.config.js && sed -i.bck 's/^var /self./' es.js && rm -rf es.js.bck", 13 | "rollup:index": "rollup --config rollup/index.config.js && sed -i.bck 's/^var /self./' index.js && rm -rf index.js.bck", 14 | "size": "echo 'async ES6' && cat async.js | brotli | wc -c && echo '' && echo 'sync ESM / ES6' && cat esm.js | brotli | wc -c && cat es.js | brotli | wc -c" 15 | }, 16 | "keywords": [ 17 | "micro", 18 | "react", 19 | "hooks", 20 | "neverland", 21 | "µland" 22 | ], 23 | "author": "Andrea Giammarchi", 24 | "license": "ISC", 25 | "devDependencies": { 26 | "@rollup/plugin-node-resolve": "^13.3.0", 27 | "ascjs": "^5.0.1", 28 | "rollup": "^2.74.1", 29 | "rollup-plugin-includepaths": "^0.2.4", 30 | "rollup-plugin-terser": "^7.0.2" 31 | }, 32 | "module": "./esm/index.js", 33 | "type": "module", 34 | "exports": { 35 | ".": { 36 | "import": "./esm/index.js", 37 | "default": "./cjs/index.js" 38 | }, 39 | "./async": { 40 | "import": "./esm/async.js", 41 | "default": "./cjs/async.js" 42 | }, 43 | "./jsx": { 44 | "import": "./esm/x.js", 45 | "default": "./cjs/x.js" 46 | }, 47 | "./package.json": "./package.json" 48 | }, 49 | "unpkg": "esm.js", 50 | "dependencies": { 51 | "jsx2tag": "^0.3.0", 52 | "uarray": "^1.0.0", 53 | "uhooks-dom": "^0.5.0", 54 | "uhtml": "^3.0.1", 55 | "umap": "^1.0.2" 56 | }, 57 | "repository": { 58 | "type": "git", 59 | "url": "git+https://github.com/WebReflection/uland.git" 60 | }, 61 | "bugs": { 62 | "url": "https://github.com/WebReflection/uland/issues" 63 | }, 64 | "homepage": "https://github.com/WebReflection/uland#readme", 65 | "description": "A uhtml based neverland fork" 66 | } 67 | -------------------------------------------------------------------------------- /rollup/async.config.js: -------------------------------------------------------------------------------- 1 | import {nodeResolve} from '@rollup/plugin-node-resolve'; 2 | import {terser} from 'rollup-plugin-terser'; 3 | import includePaths from 'rollup-plugin-includepaths'; 4 | export default { 5 | input: './esm/async.js', 6 | plugins: [ 7 | includePaths({ 8 | include: { 9 | '@ungap/create-content': 'node_modules/@ungap/degap/create-content.js', 10 | '@ungap/custom-event': 'node_modules/@ungap/degap/custom-event.js', 11 | '@webreflection/lie': 'node_modules/@ungap/degap/promise.js' 12 | } 13 | }), 14 | nodeResolve(), 15 | terser() 16 | ], 17 | context: 'null', 18 | moduleContext: 'null', 19 | output: { 20 | esModule: false, 21 | exports: 'named', 22 | file: './async.js', 23 | format: 'iife', 24 | name: 'uland' 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /rollup/es.config.js: -------------------------------------------------------------------------------- 1 | import {nodeResolve} from '@rollup/plugin-node-resolve'; 2 | import {terser} from 'rollup-plugin-terser'; 3 | 4 | export default { 5 | input: './esm/index.js', 6 | plugins: [ 7 | nodeResolve(), 8 | terser() 9 | ], 10 | context: 'null', 11 | moduleContext: 'null', 12 | output: { 13 | esModule: false, 14 | exports: 'named', 15 | file: './es.js', 16 | format: 'iife', 17 | name: 'uland' 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /rollup/esm.config.js: -------------------------------------------------------------------------------- 1 | import {nodeResolve} from '@rollup/plugin-node-resolve'; 2 | import {terser} from 'rollup-plugin-terser'; 3 | 4 | export default { 5 | input: './esm/index.js', 6 | plugins: [ 7 | nodeResolve(), 8 | terser() 9 | ], 10 | context: 'null', 11 | moduleContext: 'null', 12 | output: { 13 | file: './esm.js', 14 | format: 'module' 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /rollup/index.config.js: -------------------------------------------------------------------------------- 1 | import {nodeResolve} from '@rollup/plugin-node-resolve'; 2 | 3 | export default { 4 | input: './esm/index.js', 5 | plugins: [ 6 | nodeResolve() 7 | ], 8 | context: 'null', 9 | moduleContext: 'null', 10 | output: { 11 | esModule: false, 12 | exports: 'named', 13 | file: './index.js', 14 | format: 'iife', 15 | name: 'uland' 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /test/async.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Document 7 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /test/button.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | DOM Augmentor 8 | 37 | 38 | 39 |
40 |
41 | 42 | -------------------------------------------------------------------------------- /test/children.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | children 8 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /test/click.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /test/collapsible.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 | Loading... 11 | 67 | 68 | -------------------------------------------------------------------------------- /test/counter.mjs: -------------------------------------------------------------------------------- 1 | import registry from './registry.mjs'; 2 | 3 | const {Component, html, useState} = registry.get('@uland'); 4 | 5 | export default Component(initialState => { 6 | const [count, setCount] = useState(initialState); 7 | return html` 8 | `; 11 | }); 12 | -------------------------------------------------------------------------------- /test/effect.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /test/effect2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | require('../cjs'); -------------------------------------------------------------------------------- /test/indirect.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 13 | 14 | 15 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /test/indirect.mjs: -------------------------------------------------------------------------------- 1 | // bootstrap registry and libraries 2 | import registry from './registry.mjs'; 3 | import * as uland from 'uland-ssr'; 4 | registry.set('@uland', uland); 5 | 6 | const {render, html} = uland; 7 | 8 | const {default: Counter} = await import('./counter.mjs'); 9 | 10 | render(console.log, () => html` 11 |
12 | A bounce of counters.
13 | ${Counter(0)} ${Counter(1)} 14 |
15 | `); 16 | -------------------------------------------------------------------------------- /test/issue-10.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /test/issue-37.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | DOM Augmentor 8 | 93 | 94 | 95 |
96 |
97 | 98 | -------------------------------------------------------------------------------- /test/lfx.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 25 | 26 | -------------------------------------------------------------------------------- /test/mixed.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /test/multi-return.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "commonjs", 3 | "dependencies": { 4 | "uland-ssr": "^0.2.2" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/registry.mjs: -------------------------------------------------------------------------------- 1 | export default new Map; 2 | -------------------------------------------------------------------------------- /test/state-counter.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Document 7 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /test/swr.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /test/timer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Document 7 | 8 | 27 | 28 | -------------------------------------------------------------------------------- /test/use-mo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 55 | 56 | -------------------------------------------------------------------------------- /uland-head.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebReflection/uland/2e896aecbf30c945ad0a9503094536f76aa752fa/uland-head.jpg --------------------------------------------------------------------------------