lego-dom 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -1
- package/main.js +24 -14
- package/main.min.js +2 -2
- package/package.json +1 -1
- package/parse-lego.js +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## [1.5.0] - 2026-01-19
|
|
4
|
+
|
|
5
|
+
### Breaking Changes 🚨
|
|
6
|
+
|
|
7
|
+
- **Renamed `b-styles` to `b-stylesheets`:** The directive for injecting shared stylesheets into shadow roots has been renamed from `b-styles` to `b-stylesheets` to avoid confusion with the `style` attribute and better reflect its purpose (injecting Constructable Stylesheets).
|
|
8
|
+
```html
|
|
9
|
+
<!-- Before -->
|
|
10
|
+
<template b-styles="base theme">
|
|
11
|
+
|
|
12
|
+
<!-- After -->
|
|
13
|
+
<template b-stylesheets="base theme">
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
### Documentation
|
|
17
|
+
|
|
18
|
+
- **New "Helper Utilities" Guide:** Added a dedicated page at `/docs/guide/helpers.md` documenting core instance methods like `$ancestors`, `$emit`, `$route`, `$go`, `$vars`, `$element`, and `$registry`.
|
|
19
|
+
- **Performance Tips Updated:** Revised the "Performance Tips" section in the Directives guide to correctly compare `b-show` (toggle visibility) vs `b-if` (DOM insertion/removal) instead of the outdated `b-show` vs CSS comparison.
|
|
4
20
|
|
|
5
21
|
## [1.4.0] - 2026-01-16
|
|
6
22
|
|
package/main.js
CHANGED
|
@@ -101,6 +101,24 @@ const Lego = (() => {
|
|
|
101
101
|
const componentsToUpdate = new Set();
|
|
102
102
|
let isProcessing = false;
|
|
103
103
|
|
|
104
|
+
// Optimization: Single timer for all updated hooks instead of one per component
|
|
105
|
+
let updatedTimer = null;
|
|
106
|
+
const pendingUpdated = new Set();
|
|
107
|
+
|
|
108
|
+
const scheduleUpdatedHooks = () => {
|
|
109
|
+
if (updatedTimer) clearTimeout(updatedTimer);
|
|
110
|
+
updatedTimer = setTimeout(() => {
|
|
111
|
+
pendingUpdated.forEach(el => {
|
|
112
|
+
const state = el._studs;
|
|
113
|
+
if (state && typeof state.updated === 'function') {
|
|
114
|
+
try { state.updated.call(state); } catch (e) { console.error(`[Lego] Error in updated hook:`, e); }
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
pendingUpdated.clear();
|
|
118
|
+
updatedTimer = null;
|
|
119
|
+
}, 50); // Global debounce
|
|
120
|
+
};
|
|
121
|
+
|
|
104
122
|
return {
|
|
105
123
|
add: (el) => {
|
|
106
124
|
if (!el || isProcessing) return;
|
|
@@ -114,21 +132,13 @@ const Lego = (() => {
|
|
|
114
132
|
componentsToUpdate.clear();
|
|
115
133
|
queued = false;
|
|
116
134
|
|
|
135
|
+
// Simple synchronous render loop (faster for small batches, no overhead)
|
|
117
136
|
batch.forEach(el => render(el));
|
|
118
137
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
try {
|
|
124
|
-
state.updated.call(state);
|
|
125
|
-
} catch (e) {
|
|
126
|
-
console.error(`[Lego] Error in updated hook:`, e);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
});
|
|
130
|
-
isProcessing = false;
|
|
131
|
-
}, 0);
|
|
138
|
+
// Batch complete: Queue updated hooks efficiently
|
|
139
|
+
batch.forEach(el => pendingUpdated.add(el));
|
|
140
|
+
scheduleUpdatedHooks();
|
|
141
|
+
isProcessing = false;
|
|
132
142
|
});
|
|
133
143
|
}
|
|
134
144
|
};
|
|
@@ -540,7 +550,7 @@ const Lego = (() => {
|
|
|
540
550
|
const tpl = templateNode.content.cloneNode(true);
|
|
541
551
|
const shadow = el.attachShadow({ mode: 'open' });
|
|
542
552
|
|
|
543
|
-
const styleKeys = (templateNode.getAttribute('b-
|
|
553
|
+
const styleKeys = (templateNode.getAttribute('b-stylesheets') || "").split(/\s+/).filter(Boolean);
|
|
544
554
|
if (styleKeys.length > 0) {
|
|
545
555
|
const sheetsToApply = styleKeys.flatMap(key => styleRegistry.get(key) || []);
|
|
546
556
|
if (sheetsToApply.length > 0) {
|
package/main.min.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
const Lego=(()=>{const
|
|
1
|
+
const Lego=(()=>{const b={},M=new WeakMap,O=new WeakMap,P=new WeakMap,v=new Set,D=new Map,B=new Map,H=new Map,j=new Map;let q={};const p={onError:(e,s,c)=>{console.error(`[Lego Error] [${s}]`,e,c)},metrics:{},syntax:"brackets"},$=()=>p.syntax==="brackets"?["[[","]]"]:["{{","}}"],L=()=>{const[e,s]=$(),c=e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),o=s.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");return new RegExp(`${c}(.*?)${o}`,"g")},R=[],ee=e=>typeof e!="string"?e:e.replace(/[&<>"']/g,s=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[s]),K=e=>{const c=e.split("/").pop().replace(/\.lego$/,"").replace(/_/g,"-").replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase();if(!c.includes("-"))throw new Error(`[Lego] Invalid component definition: "${e}". Component names must contain a hyphen (e.g. user-card.lego or UserCard.lego).`);return c},V=(e,s)=>{if(typeof e=="function"){const o=Array.from(document.querySelectorAll("*")).filter(t=>t.tagName.includes("-"));return[].concat(e(o))}if(e.startsWith("#")){const o=document.getElementById(e.slice(1));return o?[o]:[]}const c=s?.querySelectorAll(e)||[];return c.length>0?[...c]:[...document.querySelectorAll(e)]},G=(e,...s)=>c=>{const o=async(t,a=null,r=!0,n={})=>{if(r){const d={legoTargets:s.filter(i=>typeof i=="string"),method:t,body:a};history.pushState(d,"",e)}await W(s.length?s:null,c)};return{get:(t=!0,a={})=>o("GET",null,t,a),post:(t,a=!0,r={})=>o("POST",t,a,r),put:(t,a=!0,r={})=>o("PUT",t,a,r),patch:(t,a=!0,r={})=>o("PATCH",t,a,r),delete:(t=!0,a={})=>o("DELETE",null,t,a)}},J=(()=>{let e=!1;const s=new Set;let c=!1,o=null;const t=new Set,a=()=>{o&&clearTimeout(o),o=setTimeout(()=>{t.forEach(r=>{const n=r._studs;if(n&&typeof n.updated=="function")try{n.updated.call(n)}catch(l){console.error("[Lego] Error in updated hook:",l)}}),t.clear(),o=null},50)};return{add:r=>{!r||c||(s.add(r),!e&&(e=!0,requestAnimationFrame(()=>{c=!0;const n=Array.from(s);s.clear(),e=!1,n.forEach(l=>x(l)),n.forEach(l=>t.add(l)),a(),c=!1})))}}})(),S=(e,s,c=J)=>{if(e===null||typeof e!="object"||e instanceof Node)return e;if(M.has(e))return M.get(e);const o={get:(a,r)=>{const n=Reflect.get(a,r);return n!==null&&typeof n=="object"&&!(n instanceof Node)?S(n,s,c):n},set:(a,r,n)=>{const l=a[r],d=Reflect.set(a,r,n);return l!==n&&c.add(s),d},deleteProperty:(a,r)=>{const n=Reflect.deleteProperty(a,r);return c.add(s),n}},t=new Proxy(e,o);return M.set(e,t),t},U=e=>{try{return new Function(`return (${e})`)()}catch(s){return console.error("[Lego] Failed to parse b-data:",e,s),{}}},N=e=>(O.has(e)||O.set(e,{snapped:!1,bindings:null,bound:!1,rendering:!1,anchor:null,hasGlobalDependency:!1}),O.get(e)),F=(e,s)=>{if(!e)return"";const c=e.trim().split(".");let o=s;for(const t of c){if(o==null)return"";o=o[t]}return o??""},Z=(e,s)=>{let c=e.parentElement||e.getRootNode().host;for(;c;){if(c.tagName&&c.tagName.toLowerCase()===s.toLowerCase())return c;c=c.parentElement||c.getRootNode&&c.getRootNode().host}},h=(e,s,c=!1)=>{if(/\b(function|eval|import|class|module|deploy|constructor|__proto__)\b/.test(e)){console.warn(`[Lego] Security Warning: Blocked dangerous expression "${e}"`);return}try{const o=s.state||{};let t=H.get(e);t||(t=new Function("global","self","event","helpers",`
|
|
2
2
|
with(helpers) {
|
|
3
3
|
with(this) {
|
|
4
4
|
return ${e}
|
|
5
5
|
}
|
|
6
6
|
}
|
|
7
|
-
`),H.set(e,t));const a={$ancestors:n=>Z(s.self,n),$registry:n=>B.get(n.toLowerCase()),$element:s.self,$route:Lego.globals.$route,$go:(n,...l)=>j(n,...l)(s.self),$emit:(n,l)=>{s.self.dispatchEvent(new CustomEvent(n,{detail:l,bubbles:!0,composed:!0}))}},r=t.call(o,s.global,s.self,s.event,a);return typeof r=="function"?r.call(o,s.event):r}catch(o){if(c)throw o;p.onError(o,"render-error",s.self);return}},z=(e,s)=>{if(e.type==="checkbox")e.checked!==!!s&&(e.checked=!!s);else{const c=s==null?"":String(s);e.value!==c&&(e.value=c)}},x=(e,s,c=null)=>{const o=s._studs,t=n=>{const l=N(n);if(!l.bound){if(n.hasAttributes()){const d=n.attributes;for(let i=0;i<d.length;i++){const u=d[i];if(u.name.startsWith("@")){const f=u.name.slice(1);n.addEventListener(f,g=>{try{let m=o;if(c){const y=h(c.listName,{state:o,global:Lego.globals,self:s})[c.index];m=Object.assign(Object.create(o),{[c.name]:y})}h(u.value,{state:m,global:Lego.globals,self:n,event:g},!0)}catch(m){p.onError(m,"event-handler",n)}})}}if(n.hasAttribute("b-sync")){const i=n.getAttribute("b-sync"),u=()=>{try{let f,g;if(c&&i.startsWith(c.name+".")){const y=h(c.listName,{state:o,global:Lego.globals,self:s})[c.index];if(!y)return;const E=i.split(".").slice(1);g=E.pop(),f=E.reduce((T,A)=>T[A],y)}else{const b=i.split(".");g=b.pop(),f=b.reduce((y,E)=>y[E],o)}const m=n.type==="checkbox"?n.checked:n.value;f&&f[g]!==m&&(f[g]=m)}catch(f){p.onError(f,"sync-update",n)}};n.addEventListener("input",u),n.addEventListener("change",u)}if(n.hasAttribute("b-var")){const i=n.getAttribute("b-var");o.$vars&&(o.$vars[i]=n)}}l.bound=!0}};e instanceof Element&&t(e);const a=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT);let r;for(;r=a.nextNode();)t(r)},Y=e=>{const s=[],c=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT|NodeFilter.SHOW_TEXT);let o;for(;o=c.nextNode();){if((r=>{let n=r.parentNode;for(;n&&n!==e;){if(n.hasAttribute&&n.hasAttribute("b-for"))return!0;n=n.parentNode}return!1})(o))continue;const a=r=>{if(/\bglobal\b/.test(r)){const n=e.host||e;N(n).hasGlobalDependency=!0}};if(o.nodeType===Node.ELEMENT_NODE){if(o.hasAttribute("b-if")){const n=o.getAttribute("b-if");a(n);const l=document.createComment(`b-if: ${n}`),d=N(o);d.anchor=l,s.push({type:"b-if",node:o,anchor:l,expr:n})}if(o.hasAttribute("b-show")){const n=o.getAttribute("b-show");a(n),s.push({type:"b-show",node:o,expr:n})}if(o.hasAttribute("b-for")){const n=o.getAttribute("b-for").match(/^\s*(\w+)\s+in\s+([\s\S]+?)\s*$/);n&&(a(n[2]),s.push({type:"b-for",node:o,itemName:n[1],listName:n[2].trim(),template:o.cloneNode(!0)}),o.innerHTML="")}if(o.hasAttribute("b-text")&&s.push({type:"b-text",node:o,path:o.getAttribute("b-text")}),o.hasAttribute("b-html")){const n=o.getAttribute("b-html");a(n),s.push({type:"b-html",node:o,expr:n})}o.hasAttribute("b-sync")&&s.push({type:"b-sync",node:o});const[r]=_();[...o.attributes].forEach(n=>{n.value.includes(r)&&(a(n.value),s.push({type:"attr",node:o,attrName:n.name,template:n.value}))})}else if(o.nodeType===Node.TEXT_NODE){const[r]=_();o.textContent.includes(r)&&(a(o.textContent),s.push({type:"text",node:o,template:o.textContent}))}}return s},Q=(e,s)=>{const c=a=>{if(a.nodeType===Node.TEXT_NODE){a._tpl===void 0&&(a._tpl=a.textContent);const r=a._tpl.replace(L(),(n,l)=>h(l.trim(),{state:s,global:Lego.globals,self:a})??"");a.textContent!==r&&(a.textContent=r)}else if(a.nodeType===Node.ELEMENT_NODE){const[r]=_();[...a.attributes].forEach(n=>{if(n._tpl===void 0&&(n._tpl=n.value),n._tpl.includes(r)){const l=n._tpl.replace(L(),(d,i)=>h(i.trim(),{state:s,global:Lego.globals,self:a})??"");n.value!==l&&(n.value=l,n.name==="class"&&(a.className=l))}})}};c(e);const o=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT|NodeFilter.SHOW_TEXT);let t;for(;t=o.nextNode();)c(t)},C=e=>{const s=e._studs;if(!s)return;const c=N(e);if(!c.rendering){c.rendering=!0,p.metrics&&p.metrics.onRenderStart&&p.metrics.onRenderStart(e);try{const o=e.shadowRoot||e;c.bindings||(c.bindings=Y(o)),c.bindings.forEach(t=>{if(t.type==="b-if"){const a=!!h(t.expr,{state:s,global:Lego.globals,self:t.node}),r=!!t.node.parentNode;a&&!r?t.anchor.parentNode&&t.anchor.parentNode.replaceChild(t.node,t.anchor):!a&&r&&t.node.parentNode.replaceChild(t.anchor,t.node)}if(t.type==="b-show"&&(t.node.style.display=h(t.expr,{state:s,global:Lego.globals,self:t.node})?"":"none"),t.type==="b-text"&&(t.node.textContent=D(t.path,s)),t.type==="b-html"&&(t.node.innerHTML=h(t.expr,{state:s,global:Lego.globals,self:t.node})||""),t.type==="b-sync"&&z(t.node,D(t.node.getAttribute("b-sync"),s)),t.type==="text"){const a=t.template.replace(L(),(r,n)=>h(n.trim(),{state:s,global:Lego.globals,self:t.node})??"");t.node.textContent!==a&&(t.node.textContent=a)}if(t.type==="attr"){const a=t.template.replace(L(),(r,n)=>h(n.trim(),{state:s,global:Lego.globals,self:t.node})??"");t.node.getAttribute(t.attrName)!==a&&(t.node.setAttribute(t.attrName,a),t.attrName==="class"&&(t.node.className=a))}if(t.type==="b-for"){const a=h(t.listName,{state:s,global:Lego.globals,self:e})||[];O.has(t.node)||O.set(t.node,new Map);const r=O.get(t.node),n=new Set;a.forEach((l,d)=>{const i=l&&typeof l=="object"?l.__id||(l.__id=Math.random()):`${d}-${l}`;n.add(i);let u=r.get(i);u||(u=t.template.cloneNode(!0),u.removeAttribute("b-for"),r.set(i,u),x(u,e,{name:t.itemName,listName:t.listName,index:d}));const f=Object.assign(Object.create(s),{[t.itemName]:l});Q(u,f),u.querySelectorAll("[b-sync]").forEach(g=>{const m=g.getAttribute("b-sync");if(m.startsWith(t.itemName+".")){const b=h(t.listName,{state:s,global:Lego.globals,self:e});z(g,D(m.split(".").slice(1).join("."),b[d]))}}),t.node.children[d]!==u&&t.node.insertBefore(u,t.node.children[d]||null)});for(const[l,d]of r.entries())n.has(l)||(d.remove(),r.delete(l))}}),s===Lego.globals&&$.forEach(t=>{N(t).hasGlobalDependency&&C(t)})}catch(o){p.onError(o,"render",e)}finally{p.metrics&&p.metrics.onRenderEnd&&p.metrics.onRenderEnd(e),c.rendering=!1}}},v=e=>{if(!e||e.nodeType!==Node.ELEMENT_NODE)return;const s=N(e),c=e.tagName.toLowerCase(),o=w[c];if(o&&!s.snapped){s.snapped=!0;const a=o.content.cloneNode(!0),r=e.attachShadow({mode:"open"}),n=(o.getAttribute("b-styles")||"").split(/\s+/).filter(Boolean);if(n.length>0){const f=n.flatMap(g=>q.get(g)||[]);f.length>0&&(r.adoptedStyleSheets=[...f])}const l=P.get(c)||{},d=I(o.getAttribute("b-data")||"{}"),i=I(e.getAttribute("b-data")||"{}");e._studs=S({...l,...d,...i,$vars:{},$element:e,$emit:(f,g)=>{e.dispatchEvent(new CustomEvent(f,{detail:g,bubbles:!0,composed:!0}))},get $route(){return Lego.globals.$route},get $go(){return Lego.globals.$go}},e),r.appendChild(a);const u=r.querySelector("style");if(u&&(u.textContent=u.textContent.replace(/\bself\b/g,":host")),x(r,e),$.add(e),C(e),[...r.children].forEach(v),typeof e._studs.mounted=="function")try{e._studs.mounted.call(e._studs)}catch(f){p.onError(f,"mounted",e)}}let t=e.parentElement;for(;t&&!t._studs;)t=t.parentElement;t&&t._studs&&x(e,t),[...e.children].forEach(v)},W=e=>{if(e._studs&&typeof e._studs.unmounted=="function")try{e._studs.unmounted.call(e._studs)}catch(s){console.error("[Lego] Error in unmounted:",s)}e.shadowRoot&&[...e.shadowRoot.children].forEach(W),$.delete(e),[...e.children].forEach(W)},F=async(e=null,s=null)=>{const c=window.location.pathname,o=window.location.search,t=R.find(d=>d.regex.test(c));if(!t)return;let a=[];if(e)a=e.flatMap(d=>V(d,s));else{const d=document.querySelector("lego-router");d&&(a=[d])}if(a.length===0)return;const r=c.match(t.regex).slice(1),n=Object.fromEntries(t.paramNames.map((d,i)=>[d,r[i]])),l=Object.fromEntries(new URLSearchParams(o));t.middleware&&!await t.middleware(n,Lego.globals)||(Lego.globals.$route.url=c+o,Lego.globals.$route.route=t.path,Lego.globals.$route.params=n,Lego.globals.$route.query=l,Lego.globals.$route.method=history.state?.method||"GET",Lego.globals.$route.body=history.state?.body||null,a.forEach(d=>{if(d){const i=document.createElement(t.tagName);d.replaceChildren(i)}}))},U={init:async(e=document.body,s={})=>{(!e||typeof e.nodeType!="number")&&(e=document.body),G=s.styles||{},p.loader=s.loader;const c=Object.entries(G).map(async([t,a])=>{const r=await Promise.all(a.map(async n=>{try{const d=await(await fetch(n)).text(),i=new CSSStyleSheet;return await i.replace(d),i}catch(l){return console.error(`[Lego] Failed to load stylesheet: ${n}`,l),null}}));q.set(t,r.filter(n=>n!==null))});await Promise.all(c),document.querySelectorAll("template[b-id]").forEach(t=>{w[t.getAttribute("b-id")]=t}),new MutationObserver(t=>t.forEach(a=>{a.addedNodes.forEach(r=>{if(r.nodeType===Node.ELEMENT_NODE){v(r);const n=r.tagName.toLowerCase();if(n.includes("-")&&!w[n]&&p.loader&&!$.has(r)){const l=p.loader(n);if(l){const d=typeof l=="string"?fetch(l).then(i=>i.text()):l;Promise.resolve(d).then(i=>U.defineSFC(i,n+".lego")).catch(i=>console.error(`[Lego] Failed to load ${n}:`,i))}}}}),a.removedNodes.forEach(r=>r.nodeType===Node.ELEMENT_NODE&&W(r))})).observe(e,{childList:!0,subtree:!0}),e._studs=Lego.globals,v(e),x(e,e),C(e),R.length>0&&(window.addEventListener("popstate",t=>{const a=t.state?.legoTargets||null;F(a)}),document.addEventListener("submit",t=>{t.preventDefault()}),document.addEventListener("click",t=>{const r=t.composedPath().find(n=>n.tagName==="A"&&(n.hasAttribute("b-target")||n.hasAttribute("b-link")));if(r){t.preventDefault();const n=r.getAttribute("href"),l=r.getAttribute("b-target"),d=l?l.split(/\s+/).filter(Boolean):[],i=r.getAttribute("b-link")!=="false";Lego.globals.$go(n,...d).get(i)}}),F())},globals:S({$route:{url:window.location.pathname,route:"",params:{},query:{},method:"GET",body:null},$go:(e,...s)=>j(e,...s)(document.body)},document.body),defineSFC:(e,s="component.lego")=>{let c="",o="{}",t="",a="",r=e;const n=/<(template|script|style)\b((?:\s+(?:[^>"']|"[^"]*"|'[^']*')*)*)>/i;for(;r;){const i=r.match(n);if(!i)break;const u=i[1].toLowerCase(),f=i[2],g=i[0],m=i.index,b=`</${u}>`,y=m+g.length,E=r.indexOf(b,y);if(E===-1){console.warn(`[Lego] Unclosed <${u}> tag in ${s}`);break}const T=r.slice(y,E);if(u==="template"){c=T.trim();const A=f.match(/b-styles=["']([^"']+)["']/);A&&(t=A[1])}else if(u==="script"){const A=T.trim(),X=A.match(/export\s+default\s+({[\s\S]*})/);o=X?X[1]:A}else u==="style"&&(a=T.trim());r=r.slice(E+b.length)}const l=K(s),d=new Function(`return ${o}`)();a&&(c=`<style>${a}</style>`+c),w[l]=document.createElement("template"),w[l].innerHTML=c,w[l].setAttribute("b-styles",t),P.set(l,d),document.querySelectorAll(l).forEach(i=>!N(i).snapped&&v(i))},define:(e,s,c={},o="")=>{const t=document.createElement("template");t.setAttribute("b-id",e),t.setAttribute("b-styles",o),t.innerHTML=s,w[e]=t,P.set(e,c);try{B.set(e.toLowerCase(),S({...c},document.body))}catch(a){p.onError(a,"define",e)}document.querySelectorAll(e).forEach(v)},getActiveComponentsCount:()=>$.size,config:p,route:(e,s,c=null)=>{const o=[],t=e.replace(/:([^\/]+)/g,(a,r)=>(o.push(r),"([^/]+)"));R.push({path:e,regex:new RegExp(`^${t}$`),tagName:s,paramNames:o,middleware:c})}};return U})();typeof window<"u"&&(window.Lego=Lego);
|
|
7
|
+
`),H.set(e,t));const a={$ancestors:n=>Z(s.self,n),$registry:n=>B.get(n.toLowerCase()),$element:s.self,$route:Lego.globals.$route,$go:(n,...l)=>G(n,...l)(s.self),$emit:(n,l)=>{s.self.dispatchEvent(new CustomEvent(n,{detail:l,bubbles:!0,composed:!0}))}},r=t.call(o,s.global,s.self,s.event,a);return typeof r=="function"?r.call(o,s.event):r}catch(o){if(c)throw o;p.onError(o,"render-error",s.self);return}},I=(e,s)=>{if(e.type==="checkbox")e.checked!==!!s&&(e.checked=!!s);else{const c=s==null?"":String(s);e.value!==c&&(e.value=c)}},C=(e,s,c=null)=>{const o=s._studs,t=n=>{const l=N(n);if(!l.bound){if(n.hasAttributes()){const d=n.attributes;for(let i=0;i<d.length;i++){const u=d[i];if(u.name.startsWith("@")){const f=u.name.slice(1);n.addEventListener(f,g=>{try{let m=o;if(c){const E=h(c.listName,{state:o,global:Lego.globals,self:s})[c.index];m=Object.assign(Object.create(o),{[c.name]:E})}h(u.value,{state:m,global:Lego.globals,self:n,event:g},!0)}catch(m){p.onError(m,"event-handler",n)}})}}if(n.hasAttribute("b-sync")){const i=n.getAttribute("b-sync"),u=()=>{try{let f,g;if(c&&i.startsWith(c.name+".")){const E=h(c.listName,{state:o,global:Lego.globals,self:s})[c.index];if(!E)return;const w=i.split(".").slice(1);g=w.pop(),f=w.reduce((T,_)=>T[_],E)}else{const y=i.split(".");g=y.pop(),f=y.reduce((E,w)=>E[w],o)}const m=n.type==="checkbox"?n.checked:n.value;f&&f[g]!==m&&(f[g]=m)}catch(f){p.onError(f,"sync-update",n)}};n.addEventListener("input",u),n.addEventListener("change",u)}if(n.hasAttribute("b-var")){const i=n.getAttribute("b-var");o.$vars&&(o.$vars[i]=n)}}l.bound=!0}};e instanceof Element&&t(e);const a=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT);let r;for(;r=a.nextNode();)t(r)},Y=e=>{const s=[],c=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT|NodeFilter.SHOW_TEXT);let o;for(;o=c.nextNode();){if((r=>{let n=r.parentNode;for(;n&&n!==e;){if(n.hasAttribute&&n.hasAttribute("b-for"))return!0;n=n.parentNode}return!1})(o))continue;const a=r=>{if(/\bglobal\b/.test(r)){const n=e.host||e;N(n).hasGlobalDependency=!0}};if(o.nodeType===Node.ELEMENT_NODE){if(o.hasAttribute("b-if")){const n=o.getAttribute("b-if");a(n);const l=document.createComment(`b-if: ${n}`),d=N(o);d.anchor=l,s.push({type:"b-if",node:o,anchor:l,expr:n})}if(o.hasAttribute("b-show")){const n=o.getAttribute("b-show");a(n),s.push({type:"b-show",node:o,expr:n})}if(o.hasAttribute("b-for")){const n=o.getAttribute("b-for").match(/^\s*(\w+)\s+in\s+([\s\S]+?)\s*$/);n&&(a(n[2]),s.push({type:"b-for",node:o,itemName:n[1],listName:n[2].trim(),template:o.cloneNode(!0)}),o.innerHTML="")}if(o.hasAttribute("b-text")&&s.push({type:"b-text",node:o,path:o.getAttribute("b-text")}),o.hasAttribute("b-html")){const n=o.getAttribute("b-html");a(n),s.push({type:"b-html",node:o,expr:n})}o.hasAttribute("b-sync")&&s.push({type:"b-sync",node:o});const[r]=$();[...o.attributes].forEach(n=>{n.value.includes(r)&&(a(n.value),s.push({type:"attr",node:o,attrName:n.name,template:n.value}))})}else if(o.nodeType===Node.TEXT_NODE){const[r]=$();o.textContent.includes(r)&&(a(o.textContent),s.push({type:"text",node:o,template:o.textContent}))}}return s},Q=(e,s)=>{const c=a=>{if(a.nodeType===Node.TEXT_NODE){a._tpl===void 0&&(a._tpl=a.textContent);const r=a._tpl.replace(L(),(n,l)=>h(l.trim(),{state:s,global:Lego.globals,self:a})??"");a.textContent!==r&&(a.textContent=r)}else if(a.nodeType===Node.ELEMENT_NODE){const[r]=$();[...a.attributes].forEach(n=>{if(n._tpl===void 0&&(n._tpl=n.value),n._tpl.includes(r)){const l=n._tpl.replace(L(),(d,i)=>h(i.trim(),{state:s,global:Lego.globals,self:a})??"");n.value!==l&&(n.value=l,n.name==="class"&&(a.className=l))}})}};c(e);const o=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT|NodeFilter.SHOW_TEXT);let t;for(;t=o.nextNode();)c(t)},x=e=>{const s=e._studs;if(!s)return;const c=N(e);if(!c.rendering){c.rendering=!0,p.metrics&&p.metrics.onRenderStart&&p.metrics.onRenderStart(e);try{const o=e.shadowRoot||e;c.bindings||(c.bindings=Y(o)),c.bindings.forEach(t=>{if(t.type==="b-if"){const a=!!h(t.expr,{state:s,global:Lego.globals,self:t.node}),r=!!t.node.parentNode;a&&!r?t.anchor.parentNode&&t.anchor.parentNode.replaceChild(t.node,t.anchor):!a&&r&&t.node.parentNode.replaceChild(t.anchor,t.node)}if(t.type==="b-show"&&(t.node.style.display=h(t.expr,{state:s,global:Lego.globals,self:t.node})?"":"none"),t.type==="b-text"&&(t.node.textContent=F(t.path,s)),t.type==="b-html"&&(t.node.innerHTML=h(t.expr,{state:s,global:Lego.globals,self:t.node})||""),t.type==="b-sync"&&I(t.node,F(t.node.getAttribute("b-sync"),s)),t.type==="text"){const a=t.template.replace(L(),(r,n)=>h(n.trim(),{state:s,global:Lego.globals,self:t.node})??"");t.node.textContent!==a&&(t.node.textContent=a)}if(t.type==="attr"){const a=t.template.replace(L(),(r,n)=>h(n.trim(),{state:s,global:Lego.globals,self:t.node})??"");t.node.getAttribute(t.attrName)!==a&&(t.node.setAttribute(t.attrName,a),t.attrName==="class"&&(t.node.className=a))}if(t.type==="b-for"){const a=h(t.listName,{state:s,global:Lego.globals,self:e})||[];P.has(t.node)||P.set(t.node,new Map);const r=P.get(t.node),n=new Set;a.forEach((l,d)=>{const i=l&&typeof l=="object"?l.__id||(l.__id=Math.random()):`${d}-${l}`;n.add(i);let u=r.get(i);u||(u=t.template.cloneNode(!0),u.removeAttribute("b-for"),r.set(i,u),C(u,e,{name:t.itemName,listName:t.listName,index:d}));const f=Object.assign(Object.create(s),{[t.itemName]:l});Q(u,f),u.querySelectorAll("[b-sync]").forEach(g=>{const m=g.getAttribute("b-sync");if(m.startsWith(t.itemName+".")){const y=h(t.listName,{state:s,global:Lego.globals,self:e});I(g,F(m.split(".").slice(1).join("."),y[d]))}}),t.node.children[d]!==u&&t.node.insertBefore(u,t.node.children[d]||null)});for(const[l,d]of r.entries())n.has(l)||(d.remove(),r.delete(l))}}),s===Lego.globals&&v.forEach(t=>{N(t).hasGlobalDependency&&x(t)})}catch(o){p.onError(o,"render",e)}finally{p.metrics&&p.metrics.onRenderEnd&&p.metrics.onRenderEnd(e),c.rendering=!1}}},A=e=>{if(!e||e.nodeType!==Node.ELEMENT_NODE)return;const s=N(e),c=e.tagName.toLowerCase(),o=b[c];if(o&&!s.snapped){s.snapped=!0;const a=o.content.cloneNode(!0),r=e.attachShadow({mode:"open"}),n=(o.getAttribute("b-stylesheets")||"").split(/\s+/).filter(Boolean);if(n.length>0){const f=n.flatMap(g=>j.get(g)||[]);f.length>0&&(r.adoptedStyleSheets=[...f])}const l=D.get(c)||{},d=U(o.getAttribute("b-data")||"{}"),i=U(e.getAttribute("b-data")||"{}");e._studs=S({...l,...d,...i,$vars:{},$element:e,$emit:(f,g)=>{e.dispatchEvent(new CustomEvent(f,{detail:g,bubbles:!0,composed:!0}))},get $route(){return Lego.globals.$route},get $go(){return Lego.globals.$go}},e),Object.defineProperty(e,"state",{get(){return this._studs},set(f){Object.assign(this._studs,f)},configurable:!0,enumerable:!1}),r.appendChild(a);const u=r.querySelector("style");if(u&&(u.textContent=u.textContent.replace(/\bself\b/g,":host")),C(r,e),v.add(e),x(e),[...r.children].forEach(A),typeof e._studs.mounted=="function")try{e._studs.mounted.call(e._studs)}catch(f){p.onError(f,"mounted",e)}}let t=e.parentElement;for(;t&&!t._studs;)t=t.parentElement;t&&t._studs&&C(e,t),[...e.children].forEach(A)},k=e=>{if(e._studs&&typeof e._studs.unmounted=="function")try{e._studs.unmounted.call(e._studs)}catch(s){console.error("[Lego] Error in unmounted:",s)}e.shadowRoot&&[...e.shadowRoot.children].forEach(k),v.delete(e),[...e.children].forEach(k)},W=async(e=null,s=null)=>{const c=window.location.pathname,o=window.location.search,t=R.find(d=>d.regex.test(c));if(!t)return;let a=[];if(e)a=e.flatMap(d=>V(d,s));else{const d=document.querySelector("lego-router");d&&(a=[d])}if(a.length===0)return;const r=c.match(t.regex).slice(1),n=Object.fromEntries(t.paramNames.map((d,i)=>[d,r[i]])),l=Object.fromEntries(new URLSearchParams(o));t.middleware&&!await t.middleware(n,Lego.globals)||(Lego.globals.$route.url=c+o,Lego.globals.$route.route=t.path,Lego.globals.$route.params=n,Lego.globals.$route.query=l,Lego.globals.$route.method=history.state?.method||"GET",Lego.globals.$route.body=history.state?.body||null,a.forEach(d=>{if(d){const i=document.createElement(t.tagName);d.replaceChildren(i)}}))},z={snap:A,unsnap:k,init:async(e=document.body,s={})=>{(!e||typeof e.nodeType!="number")&&(e=document.body),q=s.styles||{},p.loader=s.loader;const c=Object.entries(q).map(async([t,a])=>{const r=await Promise.all(a.map(async n=>{try{const d=await(await fetch(n)).text(),i=new CSSStyleSheet;return await i.replace(d),i}catch(l){return console.error(`[Lego] Failed to load stylesheet: ${n}`,l),null}}));j.set(t,r.filter(n=>n!==null))});if(await Promise.all(c),document.querySelectorAll("template[b-id]").forEach(t=>{b[t.getAttribute("b-id")]=t}),new MutationObserver(t=>t.forEach(a=>{a.addedNodes.forEach(r=>{if(r.nodeType===Node.ELEMENT_NODE){A(r);const n=r.tagName.toLowerCase();if(n.includes("-")&&!b[n]&&p.loader&&!v.has(r)){const l=p.loader(n);if(l){const d=typeof l=="string"?fetch(l).then(i=>i.text()):l;Promise.resolve(d).then(i=>z.defineSFC(i,n+".lego")).catch(i=>console.error(`[Lego] Failed to load ${n}:`,i))}}}}),a.removedNodes.forEach(r=>r.nodeType===Node.ELEMENT_NODE&&k(r))})).observe(e,{childList:!0,subtree:!0}),e._studs=Lego.globals,A(e),C(e,e),x(e),s.studio){if(!b["lego-studio"]){const t=document.createElement("script");t.src="https://unpkg.com/@legodom/studio@0.0.2/dist/lego-studio.js",t.onerror=()=>console.warn("[Lego] Failed to load Studio from CDN"),document.head.appendChild(t)}Lego.route("/_/studio","lego-studio"),Lego.route("/_/studio/:component","lego-studio")}R.length>0&&(window.addEventListener("popstate",t=>{const a=t.state?.legoTargets||null;W(a)}),document.addEventListener("submit",t=>{t.preventDefault()}),document.addEventListener("click",t=>{const r=t.composedPath().find(n=>n.tagName==="A"&&(n.hasAttribute("b-target")||n.hasAttribute("b-link")));if(r){t.preventDefault();const n=r.getAttribute("href"),l=r.getAttribute("b-target"),d=l?l.split(/\s+/).filter(Boolean):[],i=r.getAttribute("b-link")!=="false";Lego.globals.$go(n,...d).get(i)}}),W())},globals:S({$route:{url:window.location.pathname,route:"",params:{},query:{},method:"GET",body:null},$go:(e,...s)=>G(e,...s)(document.body)},document.body),defineSFC:(e,s="component.lego")=>{let c="",o="{}",t="",a="",r=e;const n=/<(template|script|style)\b((?:\s+(?:[^>"']|"[^"]*"|'[^']*')*)*)>/i;for(;r;){const i=r.match(n);if(!i)break;const u=i[1].toLowerCase(),f=i[2],g=i[0],m=i.index,y=`</${u}>`,E=m+g.length,w=r.indexOf(y,E);if(w===-1){console.warn(`[Lego] Unclosed <${u}> tag in ${s}`);break}const T=r.slice(E,w);if(u==="template"){c=T.trim();const _=f.match(/b-styles=["']([^"']+)["']/);_&&(t=_[1])}else if(u==="script"){const _=T.trim(),X=_.match(/export\s+default\s+({[\s\S]*})/);o=X?X[1]:_}else u==="style"&&(a=T.trim());r=r.slice(w+y.length)}const l=K(s),d=new Function(`return ${o}`)();a&&(c=`<style>${a}</style>`+c),b[l]=document.createElement("template"),b[l].innerHTML=c,b[l].setAttribute("b-styles",t),D.set(l,d),document.querySelectorAll(l).forEach(i=>!N(i).snapped&&A(i))},define:(e,s,c={},o="")=>{const t=document.createElement("template");t.setAttribute("b-id",e),t.setAttribute("b-styles",o),t.innerHTML=s,b[e]=t,D.set(e,c);try{B.set(e.toLowerCase(),S({...c},document.body))}catch(a){p.onError(a,"define",e)}document.querySelectorAll(e).forEach(A)},getActiveComponentsCount:()=>v.size,getLegos:()=>Object.keys(b),config:p,route:(e,s,c=null)=>{const o=[],t=e.replace(/:([^\/]+)/g,(a,r)=>(o.push(r),"([^/]+)"));R.push({path:e,regex:new RegExp(`^${t}$`),tagName:s,paramNames:o,middleware:c})}};return z})();typeof window<"u"&&(window.Lego=Lego);
|
package/package.json
CHANGED
package/parse-lego.js
CHANGED
|
@@ -56,8 +56,8 @@ export function parseLego(content, filename = 'component.lego') {
|
|
|
56
56
|
|
|
57
57
|
if (tagName === 'template') {
|
|
58
58
|
result.template = innerContent.trim();
|
|
59
|
-
// Extract b-
|
|
60
|
-
const bStylesMatch = attrs.match(/b-
|
|
59
|
+
// Extract b-stylesheets attribute if present
|
|
60
|
+
const bStylesMatch = attrs.match(/b-stylesheets=["']([^"']+)["']/);
|
|
61
61
|
if (bStylesMatch) {
|
|
62
62
|
result.stylesAttr = bStylesMatch[1];
|
|
63
63
|
}
|