@mr_hugo/boredom
Version:
Another boring JavaScript framework.
4 lines (2 loc) • 8.33 kB
JavaScript
var M=async t=>{let e=new Map;for(let n=0;n<t.length;++n){let r=L(`script[src*="${t[n]}"]`)?.getAttribute("src"),s=null;if(r)try{let o=await import(r);for(let i of Object.keys(o)){s=o[i];break}e.set(t[n],s)}catch(o){console.error(`Unable to import "${r}"`,o)}}return e},k=()=>Array.from(I("template[data-component]")).filter(t=>t instanceof HTMLElement).map(t=>{let e={name:"",attributes:[]};for(let n in t.dataset)n==="component"?e.name=t.dataset[n]??"":e.attributes.push([F(n),t.dataset[n]??""]);if(e.name==="")throw new Error(`A <template> was found with an invalid data-component: "${t.dataset.component}"`);return e}).map(({name:t,attributes:e})=>(Q(t,{attributes:e}),t)),E=(t,e)=>{let n=f(t);if(!O(n)){let r=`The tag name "${t}" is not a BoreDOM component.
"createComponent" only accepts tag-names with matching <template> tags that have a data-component attribute in them.`;throw console.error(r),new Error(r)}return e&&(n.renderCallback=e),n},w=t=>{let e=L(t);if(!(e===null||!O(e)))return e},L=t=>document.querySelector(t),I=t=>document.querySelectorAll(t),f=(t,e)=>{let n=document.createElement(t);return e&&Array.isArray(e)&&e.length>0&&e.map(r=>n.appendChild(r)),n};var D=(t,e)=>{document.readyState==="loading"?addEventListener("DOMContentLoaded",()=>dispatchEvent(new CustomEvent(t,{detail:e}))):dispatchEvent(new CustomEvent(t,{detail:e}))};var H=t=>typeof t=="object",v=t=>typeof t=="function",O=t=>H(t)&&"isBored"in t&&!!t.isBored;var F=t=>{if(t===""||!t.split("").some(n=>n!==n.toLowerCase()))return t;let e="";for(let n=0;n<t.length;n++){let r=t[n];r===r.toUpperCase()&&n!==0&&(e+="-"),e+=r.toLowerCase()}return e};var S=t=>t.startsWith("on"),z=t=>t.startsWith("queriedOn"),b=t=>S(t)?t.slice(2).toLowerCase():t.slice(9).toLowerCase(),C=class extends HTMLElement{},Q=(t,e={})=>{customElements.get(t)||customElements.define(t,class extends C{static get observedAttributes(){return typeof e.attributeChangedCallback=="object"?Object.keys(e.attributeChangedCallback):[]}constructor(){super()}isBored=!0;traverse(n,{traverseShadowRoot:r,query:s}={}){Array.from(r?this.shadowRoot?.querySelectorAll(s??"*")??[]:[]).concat(Array.from(this.querySelectorAll(s??"*"))).filter(o=>o instanceof HTMLElement).forEach(n)}#t(n){return n.split("'").filter(r=>r.length>2&&!(r.includes("(")||r.includes(",")||r.includes(")")))}#e(){let n;this.traverse(r=>{if(r instanceof HTMLElement){customElements.get(r.tagName.toLowerCase())&&(n=r);for(let o=0;o<r.attributes.length;o++){let i=r.attributes[o];if(S(i.name)){let a=this.#t(i.value);a.length>0&&a.forEach(c=>{r.addEventListener(b(i.name),l=>D(c,{event:l}))}),r.setAttribute(`data-${i.name}-dispatches`,a.join()),r.removeAttribute(i.name)}}}},{traverseShadowRoot:!0})}isInitialized=!1;#n(){let n=L(`[data-component="${t}"]`)??f("template"),r=n.getAttribute("shadowrootmode");if(e.style||e.shadow||r){let o=e.shadowrootmode??r??"open",i=this.attachShadow({mode:o});if(e.style){let a=f("style");a.textContent=e.style,i.appendChild(a)}if(e.shadow){let a=f("template");a.innerHTML=e.shadow,i.appendChild(a.content.cloneNode(!0))}else r&&i.appendChild(n.content.cloneNode(!0))}n&&!r&&this.appendChild(n.content.cloneNode(!0)),e.onSlotChange&&this.traverse(o=>{o instanceof HTMLSlotElement&&o.addEventListener("slotchange",i=>e.onSlotChange?.(i))},{traverseShadowRoot:!0}),v(e.onClick)&&this.addEventListener("click",e.onClick);for(let[o,i]of Object.entries(e))if(S(o)){if(!v(i))continue;this.addEventListener(b(o),i)}else if(z(o)){let a=i;if(!H(a))continue;let c=b(o);for(let[l,d]of Object.entries(a))this.traverse(u=>{u.addEventListener(c,d)},{traverseShadowRoot:!0,query:l})}e.attributes&&Array.isArray(e.attributes)&&e.attributes.map(([o,i])=>this.setAttribute(o,i)),this.#e(),this.isInitialized=!0}renderCallback=n=>{};connectedCallback(){this.isInitialized||this.#n(),this.renderCallback(this),e.connectedCallback?.(this)}slots=y(this);updateSlot(n,r,s){document.createElement(s).setAttribute("slot",n)}disconnectedCallback(){console.log("disconnected "+this.tagName),e.disconnectedCallback?.(this)}adoptedCallback(){console.log("adopted "+this.tagName),e.adoptedCallback?.(this)}attributeChangedCallback(n,r,s){e.attributeChangedCallback&&e.attributeChangedCallback[n]({element:this,name:n,oldValue:r,newValue:s})}})};function j(t,e){let n=e;return e===null||t.forEach(r=>{n=n[r]}),n}function R(t,e=[]){let n=[{path:[],obj:t}],r=[];for(;n.length>0;){let{path:s,obj:o}=n.pop();for(let i in o){if(e.includes(i))continue;let a=o[i],c=s.concat(i);typeof a=="object"&&a!==null&&n.push({path:c,obj:a}),r.push({path:c,value:a})}}return r}function A(t){if(t==null||typeof t!="object")return!1;let e=Object.getPrototypeOf(t);return e==null?!0:e===Object.prototype}function $(t,e){return(n,r)=>{addEventListener(n,s=>{let o=s?.detail?.event.currentTarget;for(;o;){if(o===t){r(e,s.detail);return}o instanceof HTMLElement?o=o.parentElement:o=void 0}})}}function B(t){return new Proxy({},{get(e,n,r){let s=new Error(`Ref "${String(n)}" not found in <${t.tagName}>`);if(typeof n=="string"){let o=t.querySelectorAll(`[data-ref="${n}"]`);if(!o)throw s;let i=Array.from(o).filter(a=>a instanceof HTMLElement);if(i.length===0)throw s;return i.length===1?i[0]:i}}})}function y(t){return new Proxy({},{get(e,n,r){let s=new Error(`Slot "${String(n)}" not found in <${t.tagName}>`);if(typeof n=="string"){let o=t.querySelectorAll(`slot[name="${n}"]`);if(!o)throw s;let i=Array.from(o).filter(a=>a instanceof HTMLSlotElement);if(i.length===0)throw s;return i.length===1?i[0]:i}},set(e,n,r){if(typeof n!="string")return!1;let s=r;if(r instanceof HTMLElement)r.setAttribute("data-slot",n);else if(typeof r=="string")s=f("span"),s.setAttribute("data-slot",n),s.innerText=r;else throw new Error(`Invalid value for slot ${n} in <${t.tagName}>`);let o=Array.from(t.querySelectorAll(`[data-slot="${n}"]`));return o.length>0?o.forEach(i=>i.parentElement?.replaceChild(s,i)):Array.from(t.querySelectorAll(`slot[name="${n}"]`)).forEach(a=>a.parentElement?.replaceChild(s,a)),!0}})}function x(t,e,n){let r=n||{targets:new WeakMap,path:[]};if(t!==void 0)return new Proxy(t,{set(s,o,i){return typeof o=="string"&&console.error(`State is read-only for web components. Unable to set '${o}'.`),!1},get(s,o,i){let a=Reflect.get(s,o,i),c=o==="__proto__";if(typeof o=="string"&&!c&&(r.targets.has(s)||(r.targets.set(s,r.path.join(".")),r.path.push(o))),c||Array.isArray(a)||A(a))return x(a,e,r);let l=r.targets.get(s)??"";return typeof l=="string"&&typeof o=="string"&&(l+=l!==""?`.${o}`:o,e.indexOf(l)===-1&&e.push(l)),r.path.length=0,r.path.push(l),a}})}function _(t){return()=>{let e=t.internal.updates;for(let n=0;n<e.path.length;n++){let r=e.path[n],s=e.subscribers.get(r.slice(r.indexOf(".")+1))??[];for(let o=0;o<s.length;o++)s[o](t.app)}e.path=[],e.value=[],e.raf=void 0}}function W(t){let e=t.internal,n=t;if(n===void 0)return t;let r=new WeakSet;return R(t,["internal"]).forEach(({path:s,value:o})=>{if(Array.isArray(o)||A(o)&&!r.has(o)){let a=s.join("."),c=j(s.slice(0,-1),n);if(c===o)return;c[s.at(-1)]=new Proxy(o,{set(d,u,h){return!(d[u]!==h)||(Reflect.set(d,u,h),typeof u!="string")||(e.updates.path.push(`${a}.${u}`),e.updates.value.push(d),e.updates.raf||(e.updates.raf=requestAnimationFrame(_(t)))),!0}}),r.add(o)}}),t}function N(t){let e=t.internal.customTags.filter(r=>w(r)!==void 0),n=t.internal.components;for(let[r,s]of n.entries()){if(s===null||!e.includes(r))continue;let o=w(r);if(!o){console.log(`<${r}> is not yet in the DOM. The associated JS script will be called when the component is connected.`);return}s(t,{index:0,name:r,data:void 0})(o)}}function q(t,e,n){let r=e.internal.components.get(t);if(r){let s={...n,tagName:t};return E(t,r(e,s))}return E(t)}async function ct(t){let e=k(),n=await M(e),s=W({app:t,internal:{customTags:e,components:n,updates:{path:[],value:[],raf:void 0,subscribers:new Map}}});return console.log("inflictBoreDOM()",t),N(s),s.app}function lt(t){let e=null,n;return(r,s)=>o=>{let{internal:i,app:a}=r,c=[],l=x(a,c),d=B(o),u=y(o),h=$(o,a);if(e!==o){let T=async()=>{let p=i.updates.subscribers;for(let g of c){let m=p.get(g);m?m.includes(n)||m.push(n):p.set(g,[n])}},P=t({detail:s,state:l,refs:d,on:h,self:o});n=p=>{P({state:p,refs:d,slots:u,self:o,detail:s,makeComponent:(g,m)=>q(g,r,m?.detail)}),T()}}n(l),e=o}}export{ct as inflictBoreDOM,lt as webComponent};