mono-jsx 0.6.3 → 0.6.4
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/README.md +8 -8
- package/jsx-runtime.mjs +6 -6
- package/package.json +1 -1
- package/types/html.d.ts +1 -0
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ mono-jsx is a JSX runtime that renders `<html>` element to `Response` object in
|
|
|
10
10
|
- ⚡️ Use web components, no virtual DOM
|
|
11
11
|
- 💡 Complete Web API TypeScript definitions
|
|
12
12
|
- ⏳ Streaming rendering
|
|
13
|
-
- 🗂️ Built-in router
|
|
13
|
+
- 🗂️ Built-in router(SPA mode)
|
|
14
14
|
- 🥷 [htmx](#using-htmx) integration
|
|
15
15
|
- 🌎 Universal, works in Node.js, Deno, Bun, Cloudflare Workers, etc.
|
|
16
16
|
|
|
@@ -838,7 +838,7 @@ export default {
|
|
|
838
838
|
|
|
839
839
|
## Using Router(SPA)
|
|
840
840
|
|
|
841
|
-
mono-jsx provides a built-in `<router>` element that allows your app to render components based on the current URL. On client side, it
|
|
841
|
+
mono-jsx provides a built-in `<router>` element that allows your app to render components based on the current URL. On client side, it listens all `click` events on `<a>` elements and asynchronously fetches the route component without reloading the entire page.
|
|
842
842
|
|
|
843
843
|
To use the router, you need to define your routes as a mapping of URL patterns to components and pass it to the `<html>` element as `routes` prop. The `request` prop is also required to match the current URL against the defined routes.
|
|
844
844
|
|
|
@@ -872,7 +872,7 @@ mono-jsx router requires [URLPattern](https://developer.mozilla.org/en-US/docs/W
|
|
|
872
872
|
- ✅ Cloudflare Workers
|
|
873
873
|
- ✅ Nodejs (>= 24)
|
|
874
874
|
|
|
875
|
-
For Bun users, mono-jsx provides a `monoRoutes` function that uses Bun's
|
|
875
|
+
For Bun users, mono-jsx provides a `monoRoutes` function that uses Bun's built-in routing:
|
|
876
876
|
|
|
877
877
|
```tsx
|
|
878
878
|
// bun app.tsx
|
|
@@ -911,7 +911,7 @@ function Post(this: FC) {
|
|
|
911
911
|
|
|
912
912
|
### Using DB/Storage in Route Components
|
|
913
913
|
|
|
914
|
-
Route components are always rendered on server-side, you can use any database or storage API to fetch data in your route components.
|
|
914
|
+
Route components are always rendered on server-side, you can use any database or storage API to fetch data in your route components.
|
|
915
915
|
|
|
916
916
|
```tsx
|
|
917
917
|
async function Post(this: FC) {
|
|
@@ -927,7 +927,7 @@ async function Post(this: FC) {
|
|
|
927
927
|
|
|
928
928
|
### Nav Links
|
|
929
929
|
|
|
930
|
-
Links under
|
|
930
|
+
Links under the `<nav>` element will be treated as navigation links by the router. When the `href` of a nav link matches a route, a active class will be added to the link element. By default, the active class is `active`, but you can customize it by setting the `data-active-class` attribute on the `<nav>` element. You can add style for the active link using nested CSS selectors in the `style` attribute of the `<nav>` element.
|
|
931
931
|
|
|
932
932
|
```tsx
|
|
933
933
|
export default {
|
|
@@ -948,7 +948,7 @@ export default {
|
|
|
948
948
|
|
|
949
949
|
### Fallback(404)
|
|
950
950
|
|
|
951
|
-
You can add fallback(404) content to the `<router>` element as children, which will be displayed when no route matches the current URL
|
|
951
|
+
You can add fallback(404) content to the `<router>` element as children, which will be displayed when no route matches the current URL.
|
|
952
952
|
|
|
953
953
|
```tsx
|
|
954
954
|
export default {
|
|
@@ -1044,9 +1044,9 @@ export default {
|
|
|
1044
1044
|
}
|
|
1045
1045
|
```
|
|
1046
1046
|
|
|
1047
|
-
####
|
|
1047
|
+
#### Setup htmx Manually
|
|
1048
1048
|
|
|
1049
|
-
By default, mono-jsx
|
|
1049
|
+
By default, mono-jsx imports htmx from [esm.sh](https://esm.sh/) CDN when you set the `htmx` attribute. You can also setup htmx manually with your own CDN or local copy:
|
|
1050
1050
|
|
|
1051
1051
|
```tsx
|
|
1052
1052
|
export default {
|
package/jsx-runtime.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
// runtime/index.ts
|
|
2
2
|
var CX_JS = `{var i=new Set("animation-iteration-count,aspect-ratio,border-image-outset,border-image-slice,border-image-width,box-flex-group,box-flex,box-ordinal-group,column-count,columns,fill-opacity,flex-grow,flex-negative,flex-order,flex-positive,flex-shrink,flex,flood-opacity,font-weight,grid-area,grid-column-end,grid-column-span,grid-column-start,grid-column,grid-row-end,grid-row-span,grid-row-start,grid-row,line-clamp,line-height,opacity,order,orphans,stop-opacity,stroke-dasharray,stroke-dashoffset,stroke-miterlimit,stroke-opacity,stroke-width,tab-size,widows,z-index,zoom".split(",")),r=e=>typeof e=="string",o=e=>typeof e=="object"&&e!==null;var n=e=>r(e)?e:o(e)?Array.isArray(e)?e.map(n).filter(Boolean).join(" "):Object.entries(e).filter(([,t])=>!!t).map(([t])=>t).join(" "):"";window.$cx=n;}`;
|
|
3
|
-
var STYLE_JS = `{var
|
|
3
|
+
var STYLE_JS = `{var a=new Set("animation-iteration-count,aspect-ratio,border-image-outset,border-image-slice,border-image-width,box-flex-group,box-flex,box-ordinal-group,column-count,columns,fill-opacity,flex-grow,flex-negative,flex-order,flex-positive,flex-shrink,flex,flood-opacity,font-weight,grid-area,grid-column-end,grid-column-span,grid-column-start,grid-column,grid-row-end,grid-row-span,grid-row-start,grid-row,line-clamp,line-height,opacity,order,orphans,stop-opacity,stroke-dasharray,stroke-dashoffset,stroke-miterlimit,stroke-opacity,stroke-width,tab-size,widows,z-index,zoom".split(",")),l=e=>typeof e=="string",u=e=>typeof e=="object"&&e!==null,p=e=>e.replace(/[a-z][A-Z]/g,n=>n.charAt(0)+"-"+n.charAt(1).toLowerCase());var f=e=>{let n=[],r=[],t=new d;for(let[o,i]of Object.entries(e))switch(o.charCodeAt(0)){case 58:r.push(null,o+"{"+c(i)+"}");break;case 64:r.push(o+"{",null,"{"+c(i)+"}}");break;case 38:r.push(null,o.slice(1)+"{"+c(i)+"}");break;default:n.push([o,i])}return n.length>0&&(t.inline=c(n)),r.length>0&&(t.css=r),t},g=(e,n)=>{let{inline:r,css:t}=f(n);if(t){let o="data-css-",i="["+o+(Date.now()+Math.random()).toString(36).replace(".","")+"]";document.head.appendChild(document.createElement("style")).textContent=(r?i+"{"+r+"}":"")+t.map(s=>s===null?i:s).join(""),e.getAttributeNames().forEach(s=>s.startsWith(o)&&e.removeAttribute(s)),e.setAttribute(i.slice(1,-1),"")}else r&&e.setAttribute("style",r)},c=e=>{if(u(e)){let n="";for(let[r,t]of Array.isArray(e)?e:Object.entries(e))if(l(t)||typeof t=="number"){let o=p(r),i=typeof t=="number"?a.has(o)?""+t:t+"px":""+t;n+=(n?";":"")+o+":"+(o==="content"?JSON.stringify(i):i)}return n}return""},d=(()=>{function e(){}return e.prototype=Object.freeze(Object.create(null)),e})();window.$applyStyle=g;}`;
|
|
4
4
|
var EVENT_JS = `{var w=window;w.$emit=(e,f,s)=>f.call(w.$signals?.(s)??e.target,e.type==="mount"?e.target:e);w.$onsubmit=(e,f,s)=>{e.preventDefault();f.call(w.$signals?.(s)??e.target,new FormData(e.target),e)};}`;
|
|
5
|
-
var SIGNALS_JS = `{let p;const
|
|
5
|
+
var SIGNALS_JS = `{let p;const h=window,E=new Map,b=new Map,d=e=>b.get(e)??b.set(e,S(e)).get(e),u=(e,t)=>e.getAttribute(t),y=(e,t,r)=>e.setAttribute(t,r),M=(e,t)=>e.replaceChildren(...t),v=()=>Object.create(null),S=e=>{const t=v(),r=(c,l)=>{t[c]=l},n=new Map,o=(c,l)=>{let i=n.get(c);return i||(i=new Set,n.set(c,i)),i.add(l),()=>{i.delete(l),i.size===0&&n.delete(c)}},s=new Proxy(v(),{get:(c,l)=>document.querySelector("[data-ref='"+e+":"+l+"']")});return new Proxy(t,{get:(c,l,i)=>{switch(l){case"$init":return r;case"$watch":return o;case"app":return d(0);case"refs":return s;default:return p?.(e,l),Reflect.get(c,l,i)}},set:(c,l,i,a)=>{if(i!==Reflect.get(c,l,a)){const f=n.get(l);return f&&queueMicrotask(()=>f.forEach(g=>g())),Reflect.set(c,l,i,a)}return!1}})},k=(e,t,r)=>{if(t==="toggle"){let n;return()=>{if(!n){const o=e.firstElementChild;o&&o.tagName==="TEMPLATE"&&o.hasAttribute("m-slot")?n=[...o.content.childNodes]:n=[...e.childNodes]}M(e,r()?n:[])}}if(t==="switch"){let n,o=u(e,"match"),s,c,l=i=>s.get(i)??s.set(i,[]).get(i);return()=>{if(!s){s=new Map,c=[];for(const i of e.childNodes)if(i.nodeType===1&&i.tagName==="TEMPLATE"&&i.hasAttribute("m-slot")){for(const a of i.content.childNodes)a.nodeType===1&&a.hasAttribute("slot")?l(u(a,"slot")).push(a):c.push(a);i.remove()}else o?l(o).push(i):c.push(i)}n=""+r(),M(e,s.has(n)?s.get(n):c)}}if(t&&t.length>2&&t.startsWith("[")&&t.endsWith("]")){let n=t.slice(1,-1),o=e.parentElement;return o.tagName==="M-GROUP"&&(o=o.previousElementSibling),()=>{const s=r();s===!1||s===null||s===void 0?o.removeAttribute(n):typeof s=="object"&&s!==null&&(n==="class"||n==="style"||n==="props")?n==="class"?y(o,n,$cx(s)):n==="style"?$applyStyle(o,s):y(o,n,JSON.stringify(s)):y(o,n,s===!0?"":s)}}return()=>e.textContent=""+r()},N=e=>{const t=e.indexOf(":");return t>0?[Number(e.slice(0,t)),e.slice(t+1)]:null},w=async e=>{const t=e();return t!==void 0?t:(await new Promise(r=>setTimeout(r,0)),w(e))},T=(e,t)=>customElements.define(e,class extends HTMLElement{disposes=[];connectedCallback(){t(this)}disconnectedCallback(){this.disposes.forEach(r=>r()),this.disposes.length=0}});T("m-signal",e=>{const t=d(Number(u(e,"scope"))),r=u(e,"key");if(r)e.disposes.push(t.$watch(r,k(e,u(e,"mode"),()=>t[r])));else{const n=Number(u(e,"computed"));w(()=>E.get(n)).then(([o,s])=>{const c=k(e,u(e,"mode"),o.bind(t));s.forEach(l=>{const[i,a]=N(l);e.disposes.push(d(i).$watch(a,c))})})}}),T("m-effect",e=>{const{disposes:t}=e,r=Number(u(e,"scope")),n=Number(u(e,"n")),o=new Array(n);t.push(()=>{o.forEach(s=>typeof s=="function"&&s()),o.length=0});for(let s=0;s<n;s++){const c="$ME_"+r+"_"+s;w(()=>h[c]).then(l=>{const i=[],a=d(r),f=()=>{o[s]=l.call(a)};p=(g,m)=>i.push([g,m]),f(),p=void 0;for(const[g,m]of i)t.push(d(g).$watch(m,f))},()=>{})}}),h.$signals=e=>e!==void 0?d(e):void 0,h.$MS=(e,t)=>{const[r,n]=N(e);d(r).$init(n,t)},h.$MC=(e,t,r)=>{E.set(e,[t,r])},h.$patch=(e,...t)=>{for(const[r,...n]of t){const o=n.pop();let s=e;for(const c of n)s=s[c];s[o]=r}return e};}`;
|
|
6
6
|
var SUSPENSE_JS = `{const s=new Map,i=e=>e.getAttribute("chunk-id"),l=(e,t)=>customElements.define(e,class extends HTMLElement{connectedCallback(){t(this)}});l("m-portal",e=>{s.set(i(e),e)}),l("m-chunk",e=>{setTimeout(()=>{const t=e.firstChild?.content.childNodes,o=i(e),n=s.get(o);n&&(e.hasAttribute("next")?n.before(...t):(s.delete(o),e.hasAttribute("done")?n.remove():n.replaceWith(...t)),e.remove())})});}`;
|
|
7
7
|
var LAZY_JS = `{const r=document,o=(t,e)=>t.getAttribute(e),i=(t,e=[])=>t.replaceChildren(...e);customElements.define("m-component",class extends HTMLElement{static observedAttributes=["name","props"];#e;#i;#n;#t;#s;async#r(){const t={"x-component":this.#e,"x-props":this.#i??"{}","x-runtime-flag":""+$runtimeFlag,"x-scope-seq":""+$scopeSeq},e=new AbortController;this.#s?.abort(),this.#s=e,i(this,this.#n);const s=await fetch(location.href,{headers:t,signal:e.signal});if(!s.ok)throw i(this),new Error("Failed to fetch component '"+name+"'");const[l,n]=await s.json();this.innerHTML=l,n&&(r.body.appendChild(r.createElement("script")).textContent=n)}connectedCallback(){setTimeout(()=>{if(!this.#e){const t=o(this,"name"),e=o(this,"props");if(!t)throw new Error("Component name is required");this.#e=t,this.#i=e?.startsWith("base64,")?atob(e.slice(7)):null,this.#n=[...this.childNodes]}this.#r()})}disconnectedCallback(){i(this,this.#n),this.#s?.abort(),this.#t&&clearTimeout(this.#t),this.#s=void 0,this.#t=void 0}attributeChangedCallback(t,e,s){this.#e&&s&&e!==s&&(t==="name"?this.#e=s:t==="props"&&(this.#i=s),this.#t&&clearTimeout(this.#t),this.#t=setTimeout(()=>{this.#t=void 0,this.#r()},20))}});}`;
|
|
8
|
-
var ROUTER_JS = `{const n=document,a=t=>t.split("#",1)[0];customElements.define("m-router",class extends HTMLElement{#t;#e;#s;#i;async#o(t){const i={"x-route":"true","x-runtime-flag":""+$runtimeFlag,"x-scope-seq":""+$scopeSeq}
|
|
8
|
+
var ROUTER_JS = `{const n=document,a=t=>t.split("#",1)[0];customElements.define("m-router",class extends HTMLElement{#t;#e;#s;#i;async#o(t){const i=new AbortController,e={"x-route":"true","x-runtime-flag":""+$runtimeFlag,"x-scope-seq":""+$scopeSeq};this.#i?.abort(),this.#i=i;const s=await fetch(t,{headers:e,signal:i.signal});if(s.status===404){this.replaceChildren(...this.#t);return}if(!s.ok)throw this.replaceChildren(),new Error("Failed to fetch route: "+s.status+" "+s.statusText);const[o,r]=await s.json();this.innerHTML=o,r&&(n.body.appendChild(n.createElement("script")).textContent=r)}#n(){n.querySelectorAll("nav a").forEach(t=>{const{href:i,classList:e}=t,s=t.closest("nav")?.getAttribute("data-active-class")??"active";a(i)===a(location.href)?e.add(s):e.remove(s)})}#a(t){this.#o(t),this.#n()}connectedCallback(){setTimeout(()=>{if(!this.#t)if(this.getAttribute("status")==="404")this.#t=[...this.childNodes];else{this.#t=[];for(const t of this.childNodes)if(t.nodeType===1&&t.tagName==="TEMPLATE"&&t.hasAttribute("m-slot")){this.#t.push(...t.content.childNodes),t.remove();break}}}),this.#e=t=>{if(t.defaultPrevented||t.altKey||t.ctrlKey||t.metaKey||t.shiftKey||!(t.target instanceof HTMLAnchorElement))return;const{download:i,href:e,rel:s,target:o}=t.target;i||s==="external"||o==="_blank"||!e||!e.startsWith(location.origin)||a(e)===a(location.href)||(t.preventDefault(),history.pushState({},"",e),this.#a(e))},this.#s=()=>this.#a(location.href),addEventListener("popstate",this.#s),n.addEventListener("click",this.#e),setTimeout(()=>this.#n())}disconnectedCallback(){removeEventListener("popstate",this.#s),n.removeEventListener("click",this.#e),this.#i?.abort(),this.#s=void 0,this.#e=void 0,this.#i=void 0}});}`;
|
|
9
9
|
|
|
10
10
|
// runtime/utils.ts
|
|
11
11
|
var regexpHtmlSafe = /["'&<>]/;
|
|
@@ -123,7 +123,7 @@ var $html = Symbol.for("jsx.html");
|
|
|
123
123
|
var $signal = Symbol.for("mono.signal");
|
|
124
124
|
|
|
125
125
|
// version.ts
|
|
126
|
-
var VERSION = "0.6.
|
|
126
|
+
var VERSION = "0.6.4";
|
|
127
127
|
|
|
128
128
|
// render.ts
|
|
129
129
|
var RUNTIME_CX = 1;
|
|
@@ -308,7 +308,7 @@ async function render(node, options, write, writeJS, componentMode) {
|
|
|
308
308
|
runtimeFlag |= RUNTIME_EVENT;
|
|
309
309
|
js += EVENT_JS;
|
|
310
310
|
}
|
|
311
|
-
if (signals.store.size
|
|
311
|
+
if ((signals.store.size > 0 || rc.mcs.size > 0 || signals.effects.length > 0) && !(runtimeFlag & RUNTIME_SIGNALS)) {
|
|
312
312
|
runtimeFlag |= RUNTIME_SIGNALS;
|
|
313
313
|
js += SIGNALS_JS;
|
|
314
314
|
}
|
|
@@ -937,7 +937,7 @@ function computedProps({ fcCtx }, props) {
|
|
|
937
937
|
});
|
|
938
938
|
if (patches.length > 0) {
|
|
939
939
|
const { scopeId } = fcCtx;
|
|
940
|
-
const compute = "()=>$
|
|
940
|
+
const compute = "()=>$patch(" + JSON.stringify(staticProps) + ",[" + patches.join("],[") + "])";
|
|
941
941
|
return Signal(scopeId, { compute, deps }, staticProps);
|
|
942
942
|
}
|
|
943
943
|
}
|
package/package.json
CHANGED