mono-jsx 0.8.2 → 0.9.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/README.md CHANGED
@@ -559,11 +559,11 @@ function Counter(
559
559
  You can define app signals by adding the `app` prop to the root `<html>` element. The app signals are available in all components via `this.app.<SignalName>`. Changes to the app signals will trigger re-renders in all components that use them:
560
560
 
561
561
  ```tsx
562
- interface AppSignals {
562
+ interface IAppSignals {
563
563
  themeColor: string;
564
564
  }
565
565
 
566
- function Header(this: App<FC, AppSignals>) {
566
+ function Header(this: WithAppSignals<FC, IAppSignals>) {
567
567
  return (
568
568
  <header>
569
569
  <h1 style={{ color: this.app.themeColor }}>Welcome to mono-jsx!</h1>
@@ -571,7 +571,7 @@ function Header(this: App<FC, AppSignals>) {
571
571
  )
572
572
  }
573
573
 
574
- function Footer(this: App<FC, AppSignals>) {
574
+ function Footer(this: WithAppSignals<FC, IAppSignals>) {
575
575
  return (
576
576
  <footer>
577
577
  <p style={{ color: this.app.themeColor }}>(c) 2025 mono-jsx.</p>
@@ -579,7 +579,7 @@ function Footer(this: App<FC, AppSignals>) {
579
579
  )
580
580
  }
581
581
 
582
- function Main(this: App<FC, AppSignals>) {
582
+ function Main(this: WithAppSignals<FC, IAppSignals>) {
583
583
  return (
584
584
  <main>
585
585
  <p>
@@ -879,20 +879,31 @@ The `this` object has the following built-in properties:
879
879
  - `request`: The request object from the `fetch` handler.
880
880
  - `session`: The session storage.
881
881
  - `refs`: A map of refs defined in the component.
882
- - `computed`: A method to create a computed signal.
883
- - `effect`: A method to create side effects.
882
+ - `computed(fn)`: A method to create a computed signal.
883
+ - `$(fn)`: A shortcut for `computed(fn)`.
884
+ - `effect(fn)`: A method to create side effects.
884
885
 
885
886
  ```ts
886
- type FC<Signals = {}, Refs = {}, AppSignals = {}, AppRefs = {}, Context = {}> = {
887
+ type FC<Signals = {}, Refs = {}> = {
887
888
  readonly app: AppSignals & { refs: AppRefs; url: WithParams<URL> }
888
889
  readonly context: Context;
889
890
  readonly request: WithParams<Request>;
890
891
  readonly session: Session;
891
892
  readonly refs: Refs;
892
893
  readonly computed: <T = unknown>(fn: () => T) => T;
893
- readonly $: FC["computed"];
894
+ readonly $: FC["computed"]; // A shortcut for `FC.computed`.
894
895
  readonly effect: (fn: () => void | (() => void)) => void;
895
896
  } & Signals;
897
+
898
+ // define `AppSignals` type
899
+ function Component(this: WithAppSignals<FC, { title: string }>) {
900
+ this.app.title // type: 'string'
901
+ }
902
+
903
+ // define `Context` type
904
+ function Component(this: WithContext<FC, { secret: string }>) {
905
+ this.context.secret // type: 'string'
906
+ }
896
907
  ```
897
908
 
898
909
  ### Using Signals
@@ -904,7 +915,7 @@ See the [Using Signals](#using-signals) section for more details on how to use s
904
915
  You can use `this.refs` to access refs in your components. Refs are defined using the `ref` attribute in JSX, and they allow you to access DOM elements directly. The `refs` object is a map of ref names to DOM elements.
905
916
 
906
917
  ```tsx
907
- function App(this: Refs<FC, { input?: HTMLInputElement }>) {
918
+ function App(this: WithRefs<FC, { input?: HTMLInputElement }>) {
908
919
  this.effect(() => {
909
920
  this.refs.input?.addEventListener("input", (evt) => {
910
921
  console.log("Input changed:", evt.target.value);
@@ -946,7 +957,7 @@ The `<component>` element also supports the `ref` attribute, which allows you to
946
957
  ```tsx
947
958
  import type { ComponentElement } from "mono-jsx";
948
959
 
949
- function App(this: Refs<FC, { component: ComponentElement }>) {
960
+ function App(this: WithRefs<FC, { component: ComponentElement }>) {
950
961
  this.effect(() => {
951
962
  // updating the component name and props will trigger a re-render of the component
952
963
  this.refs.component.name = "Foo";
@@ -971,7 +982,7 @@ function App(this: Refs<FC, { component: ComponentElement }>) {
971
982
  You can use the `context` property in `this` to access context values in your components. The context is defined on the root `<html>` element:
972
983
 
973
984
  ```tsx
974
- function Dash(this: Context<FC, { auth: { uuid: string; name: string } }>) {
985
+ function Dash(this: WithContext<FC, { auth: { uuid: string; name: string } }>) {
975
986
  const { auth } = this.context;
976
987
  return (
977
988
  <div>
package/jsx-runtime.mjs CHANGED
@@ -26,7 +26,7 @@ var STYLE_JS = `{var f=/^(-|f[lo].*[^se]$|g.{5,}[^ps]$|z|o[pr]|(W.{5})?[lL]i.*(t
26
26
  var RENDER_ATTR_JS = `{var s=(l,n,r)=>{let e=l.parentElement;return e.tagName==="M-GROUP"&&(e=e.previousElementSibling),()=>{let t=r();n==="value"?e.value=String(t):n==="checked"?e.checked=!!t:typeof t=="boolean"?e.toggleAttribute(n,t):t==null?e.removeAttribute(n):typeof t=="object"?n==="class"?e.setAttribute(n,$cx(t)):n==="style"?$applyStyle(e,t):e.setAttribute(n,JSON.stringify(t)):e.setAttribute(n,String(t))}};window.$renderAttr=s;}`;
27
27
  var RENDER_TOGGLE_JS = `{var i=(e,s)=>{let t,l=()=>e.replaceChildren(...s()?t:[]);return()=>{if(!t){let n=e.firstElementChild;n&&n.tagName==="TEMPLATE"&&n.hasAttribute("m-slot")?t=[...n.content.childNodes]:t=[...e.childNodes]}e.hasAttribute("vt")&&document.startViewTransition?document.startViewTransition(l):l()}};window.$renderToggle=i;}`;
28
28
  var RENDER_SWITCH_JS = `{var a=(l,u)=>{let s,r=l.getAttribute("value"),t,i,o=e=>t.get(e)??t.set(e,[]).get(e),d=()=>l.replaceChildren(...t.has(s)?t.get(s):i);return()=>{if(!t){t=new Map,i=[];for(let e of l.childNodes)if(e.nodeType===1&&e.tagName==="TEMPLATE"&&e.hasAttribute("m-slot")){for(let n of e.content.childNodes)n.nodeType===1&&n.hasAttribute("slot")?o(n.getAttribute("slot")).push(n):i.push(n);e.remove()}else r?o(r).push(e):i.push(e)}s=""+u(),l.hasAttribute("vt")&&document.startViewTransition?document.startViewTransition(d):d()}};window.$renderSwitch=a;}`;
29
- var SIGNALS_JS = `{let h;const w=window,p=document,y=new Map,k=new Map,$=new Map,u=n=>y.get(n)??y.set(n,N(n)).get(n),v=()=>Object.create(null),l=(n,e)=>n.getAttribute(e),N=n=>{const e=v(),t=(r,c)=>{e[r]=c},s=new Map,a=(r,c)=>{let i=s.get(r);return i||(i=new Set,s.set(r,i)),i.add(c),()=>{i.delete(c),i.size===0&&s.delete(r)}},o=new Proxy(v(),{get:(r,c)=>p.querySelector("[data-ref='"+n+":"+c+"']")});return new Proxy(e,{get:(r,c,i)=>{switch(c){case"$init":return t;case"$watch":return a;case"app":return u(0);case"refs":return o;default:return h?.(n,c),Reflect.get(r,c,i)}},set:(r,c,i,f)=>{if(i!==Reflect.get(r,c,f)){const d=s.get(c);return d&&queueMicrotask(()=>d.forEach(g=>g())),Reflect.set(r,c,i,f)}return!1}})},E=(n,e,t)=>{switch(e){case"toggle":return $renderToggle(n,t);case"switch":return $renderSwitch(n,t);case"html":return()=>n.innerHTML=""+t()}if(e&&e.length>2&&e.startsWith("[")&&e.endsWith("]"))return $renderAttr(n,e.slice(1,-1),t);const s=n.parentElement,a=()=>n.textContent=""+t();if(p.startViewTransition&&s.hasAttribute("data-vt")){const o=s.getAttribute("data-vt");return o&&(s.style.viewTransitionName=o),()=>p.startViewTransition(a)}return a},S=n=>{const e=n.indexOf(":");return e>0?[Number(n.slice(0,e)),n.slice(e+1)]:null},b=async(n,e=100)=>{const t=n();if(t!==void 0)return t;if(await new Promise(s=>setTimeout(s,25)),e<=0)throw new Error("Deferred value not found");return b(n,e-1)},T=n=>typeof n=="function"&&n(),M=(n,e)=>customElements.define(n,class extends HTMLElement{disposes=[];connectedCallback(){e(this)}disconnectedCallback(){this.disposes.forEach(t=>t()),this.disposes.length=0}});M("m-signal",n=>{const e=Number(l(n,"scope")),t=u(e),s=l(n,"key");if(s)n.disposes.push(t.$watch(s,E(n,l(n,"mode"),()=>t[s])));else{const a=Number(l(n,"computed"));b(()=>k.get(e+":"+a)).then(o=>{const r=E(n,l(n,"mode"),$fmap.get(a).bind(t));o.forEach(c=>{const[i,f]=S(c);n.disposes.push(u(i).$watch(f,r))})})}}),M("m-effect",n=>{const{disposes:e}=n,t=Number(l(n,"scope"));b(()=>$.get(t)??void 0).then(s=>{const a=s.length,o=new Array(a);e.push(()=>{o.forEach(T),o.length=0});for(let r=0;r<a;r++){const c=[],i=u(t),f=$fmap.get(s[r]),d=()=>{T(o[r]),o[r]=f?.call(i)};h=(g,m)=>c.push([g,m]),d(),h=void 0;for(const[g,m]of c)e.push(u(g).$watch(m,d))}})}),w.$S=(n,e)=>{const[t,s]=S(n);u(t).$init(s,e)},w.$C=(n,e,t)=>{k.set(n+":"+e,t)},w.$E=(n,...e)=>{$.set(n,e)},w.$patch=(n,...e)=>{for(const[t,...s]of e){let a=s.pop(),o=n;for(const r of s)o=o[r];o[a]=t}return n},w.$signals=n=>n!==void 0?u(n):void 0;}`;
29
+ var SIGNALS_JS = `{let h;const a=window,b=document,y=new Map,E=new Map,k=new Map,v=new Map,$=new Map,m=new Map,l=n=>y.get(n)??y.set(n,V(n)).get(n),F=()=>Object.create(null),p=(n,e)=>n.getAttribute(e),V=n=>{const e=F(),t=(o,c)=>{e[o]=c},s=new Map,r=(o,c)=>{let u=s.get(o);return u||(u=new Set,s.set(o,u)),u.add(c),()=>{u.delete(c),u.size===0&&s.delete(o)}},i=new Proxy(F(),{get:(o,c)=>b.querySelector("[data-ref='"+n+":"+c+"']")});return new Proxy(e,{get:(o,c,u)=>{switch(c){case"$init":return t;case"$watch":return r;case"app":return l(0);case"refs":return i;default:return h?.(n,c),Reflect.get(o,c,u)}},set:(o,c,u,g)=>{if(u!==Reflect.get(o,c,g)){const f=s.get(c);return f&&queueMicrotask(()=>f.forEach(d=>d())),Reflect.set(o,c,u,g)}return!1}})},M=(n,e,t)=>{switch(e){case"toggle":return $renderToggle(n,t);case"switch":return $renderSwitch(n,t);case"html":return()=>n.innerHTML=""+t()}if(e&&e.length>2&&e.startsWith("[")&&e.endsWith("]"))return $renderAttr(n,e.slice(1,-1),t);const s=n.parentElement,r=()=>n.textContent=""+t();if(b.startViewTransition&&s.hasAttribute("data-vt")){const i=s.getAttribute("data-vt");return i&&(s.style.viewTransitionName=i),()=>b.startViewTransition(r)}return r},S=n=>{const e=n.indexOf(":");return e>0?[Number(n.slice(0,e)),n.slice(e+1)]:null},q=(n,e,t,s)=>{const r=n.get(t);if(r!==void 0){s(r);return}const i=e.get(t);i?i.push(s):e.set(t,[s])},N=(n,e)=>{const t=a.$fmap?.get(n);if(t){e(t);return}const s=m.get(n);s?s.push(e):m.set(n,[e])};if(typeof a.$F=="function"){const n=a.$F;a.$F=(e,t)=>{n(e,t);const s=m.get(e);s&&(m.delete(e),s.forEach(r=>r(t)))}}const T=n=>typeof n=="function"&&n(),A=(n,e)=>customElements.define(n,class extends HTMLElement{disposes=[];connectedCallback(){e(this)}disconnectedCallback(){this.disposes.forEach(t=>t()),this.disposes.length=0}});A("m-signal",n=>{const e=Number(p(n,"scope")),t=l(e),s=p(n,"key");if(s)n.disposes.push(t.$watch(s,M(n,p(n,"mode"),()=>t[s])));else{const r=Number(p(n,"computed")),i=e+":"+r;q(E,v,i,o=>{N(r,c=>{const u=M(n,p(n,"mode"),c.bind(t));o.forEach(g=>{const[f,d]=S(g);n.disposes.push(l(f).$watch(d,u))})})})}}),A("m-effect",n=>{const{disposes:e}=n,t=Number(p(n,"scope"));q(k,$,t,s=>{const r=s.length,i=new Array(r);e.push(()=>{i.forEach(T),i.length=0});for(let o=0;o<r;o++)N(s[o],c=>{const u=[],g=l(t),f=()=>{T(i[o]),i[o]=c.call(g)};h=(d,w)=>u.push([d,w]),f(),h=void 0;for(const[d,w]of u)e.push(l(d).$watch(w,f))})})}),a.$S=(n,e)=>{const[t,s]=S(n);l(t).$init(s,e)},a.$C=(n,e,t)=>{const s=n+":"+e;E.set(s,t);const r=v.get(s);r&&(v.delete(s),r.forEach(i=>i(t)))},a.$E=(n,...e)=>{k.set(n,e);const t=$.get(n);t&&($.delete(n),t.forEach(s=>s(e)))},a.$patch=(n,...e)=>{for(const[t,...s]of e){let r=s.pop(),i=n;for(const o of s)i=i[o];i[r]=t}return n},a.$signals=n=>n!==void 0?l(n):void 0;}`;
30
30
  var SUSPENSE_JS = `{const i=new Map,o=e=>e.getAttribute("chunk-id"),l=(e,t)=>customElements.define(e,class extends HTMLElement{connectedCallback(){t(this)}});l("m-portal",e=>{i.set(o(e),e)}),l("m-chunk",e=>{setTimeout(()=>{const t=o(e),n=i.get(t),s=e.firstChild?.content.childNodes;n&&(e.hasAttribute("next")?s&&n.before(...s):(e.hasAttribute("done")?n.remove():s&&n.replaceWith(...s),i.delete(t)),e.remove())})});}`;
31
31
  var COMPONENT_JS = `{const e=document,a=(t,s)=>t.getAttribute(s);customElements.define("m-component",class extends HTMLElement{static observedAttributes=["name","props"];#t;#s;#r;#h;#i;#e=new Map;#a=!0;async#l(){if(!this.#t){this.#n("");return}const t=this.#s||"{}",s=this.#t+t,i={"x-component":this.#t,"x-props":t,"x-flags":$FLAGS},n=new AbortController;if(this.#h?.abort(),this.#h=n,this.#e.has(s)){this.#n(this.#e.get(s));return}this.#r?.length&&this.#n(this.#r);const r=await fetch(location.href,{headers:i,signal:n.signal});if(!r.ok)throw this.#n(""),new Error("Failed to fetch component '"+this.#t+"'");const[h,o]=await r.json();this.#e.set(s,h),this.#n(h),o&&(e.body.appendChild(e.createElement("script")).textContent=o)}#n(t){const s=()=>typeof t=="string"?this.innerHTML=t:this.replaceChildren(...t);this.hasAttribute("vt")&&e.startViewTransition&&!this.#a?e.startViewTransition(s):s(),this.#a=!1}get name(){return this.#t??null}set name(t){t&&t!==this.#t&&(this.#t=t,this.#o())}get props(){return this.#s?JSON.parse(this.#s):void 0}set props(t){const s=typeof t=="string"?t:JSON.stringify(t);s&&s!==this.#s&&(this.#s=s,this.#o())}attributeChangedCallback(t,s,i){this.#t&&i&&(t==="name"?this.name=i:t==="props"&&(this.props=i))}connectedCallback(){setTimeout(()=>{if(!this.#r){const t=a(this,"props");this.#t=a(this,"name"),this.#s=t?.startsWith("base64,")?atob(t.slice(7)):void 0,this.#r=[...this.childNodes]}this.#l()})}disconnectedCallback(){this.#e.clear(),this.#h?.abort(),this.#h=void 0,this.#i&&clearTimeout(this.#i),this.#i=void 0}#o(){this.#i&&clearTimeout(this.#i),this.#i=setTimeout(()=>{this.#i=void 0,this.#l()},50)}refresh(){this.#t&&this.#e.delete(this.#t+(this.#s||"{}")),this.#o()}});}`;
32
32
  var ROUTER_JS = `{const n=document,a=location,o=t=>t.split("#",1)[0],l=t=>o(t)===o(a.href);customElements.define("m-router",class extends HTMLElement{#t;#s;#i;#n;#e=new Map;#r=!0;async#c(t){const s=new AbortController,i={"x-route":"true","x-flags":$FLAGS};this.#n?.abort(),this.#n=s;const e=await fetch(t,{headers:i,signal:s.signal});if(e.status===404)return null;if(!e.ok)throw this.replaceChildren(),new Error("Failed to fetch route: "+e.status+" "+e.statusText);return e.json()}#a(t){const s=()=>typeof t=="string"?this.innerHTML=t:this.replaceChildren(...t);this.hasAttribute("vt")&&n.startViewTransition&&!this.#r?n.startViewTransition(s):s(),this.#r=!1}#o(){n.querySelectorAll("nav a").forEach(t=>{const{href:s,classList:i}=t,e=t.closest("nav")?.getAttribute("data-active-class")??"active";l(s)?i.add(e):i.remove(e)})}async#l(t,s){const i=this.#c(t).then(e=>{if(e){const[r,c]=e;return this.#e.set(t,r),this.#a(r),c}else this.#e.delete(t),this.#a(this.#t??[]),typeof $signals<"u"&&($signals(0).url=new URL(t))});this.#e.has(t)?this.#a(this.#e.get(t)):await i,history[s?.replace?"replaceState":"pushState"]({},"",t),this.#o(),window.scrollTo(0,0),i.then(e=>{e&&(n.body.appendChild(n.createElement("script")).textContent=e)})}navigate(t,s){const i=new URL(t,a.href);if(i.origin!==a.origin){a.href=t;return}l(i.href)||this.#l(t,s)}connectedCallback(){setTimeout(()=>{if(!this.#t)if(this.hasAttribute("fallback"))this.removeAttribute("fallback"),this.#t=[...this.childNodes];else{this.#t=[];for(const t of this.childNodes)if(t.nodeType===1&&t.tagName==="TEMPLATE"&&t.hasAttribute("m-fallback")){this.#t.push(...t.content.childNodes),t.remove();break}}}),this.#s=t=>{if(t.defaultPrevented||t.altKey||t.ctrlKey||t.metaKey||t.shiftKey||!(t.target instanceof HTMLAnchorElement))return;const{download:s,href:i,rel:e,target:r}=t.target;s||e==="external"||r==="_blank"||!i.startsWith(a.origin)||(t.preventDefault(),this.navigate(i))},this.#i=()=>this.#l(a.href),addEventListener("popstate",this.#i),n.addEventListener("click",this.#s),setTimeout(()=>this.#o()),globalThis.$router=this}disconnectedCallback(){removeEventListener("popstate",this.#i),n.removeEventListener("click",this.#s),delete globalThis.$router,this.#n?.abort(),this.#n=void 0,this.#e.clear(),this.#s=void 0,this.#i=void 0}});}`;
@@ -176,7 +176,7 @@ var $vnode = Symbol.for("jsx.vnode");
176
176
  var $setup = Symbol.for("mono.setup");
177
177
 
178
178
  // version.ts
179
- var VERSION = "0.8.1";
179
+ var VERSION = "0.8.2";
180
180
 
181
181
  // render.ts
182
182
  var FunctionIdGenerator = class extends Map {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mono-jsx",
3
- "version": "0.8.2",
3
+ "version": "0.9.0",
4
4
  "description": "`<html>` as a `Response`.",
5
5
  "type": "module",
6
6
  "module": "./index.mjs",
package/types/jsx.d.ts CHANGED
@@ -106,12 +106,12 @@ declare global {
106
106
  effect(fn: () => (() => void) | void): void;
107
107
  }
108
108
  & FCExtension<FC>
109
- & Omit<Omit<Signals, "init" | "refs" | "computed" | "$" | "effect">, keyof FCExtension>;
109
+ & Omit<Omit<Signals, "refs" | "init" | "computed" | "$" | "effect">, keyof FCExtension>;
110
110
 
111
111
  /**
112
112
  * Defines the `this.refs` type.
113
113
  */
114
- type Refs<T, R extends {}> = T extends FC<infer S> ? FC<S, R> : never;
114
+ type WithRefs<T, R extends {}> = T extends FC<infer S> ? FC<S, R> : never;
115
115
 
116
116
  /**
117
117
  * The JSX namespace object.
package/types/mono.d.ts CHANGED
@@ -279,7 +279,7 @@ declare global {
279
279
  /**
280
280
  * Defines the `this.app` type.
281
281
  */
282
- type App<T, AppSignals = {}, AppRefs = Record<string, HTMLElement>> = T extends FC<infer S, infer R> ? FC<S, R> & {
282
+ type WithAppSignals<T, AppSignals = {}, AppRefs = Record<string, HTMLElement>> = T extends FC<infer S, infer R> ? FC<S, R> & {
283
283
  /**
284
284
  * The global signals shared across the application.
285
285
  */
@@ -302,7 +302,7 @@ declare global {
302
302
  /**
303
303
  * Defines the `this.context` type.
304
304
  */
305
- type Context<T, Context = {}> = T extends FC<infer S, infer R> ? FC<S, R> & {
305
+ type WithContext<T, Context = {}> = T extends FC<infer S, infer R> ? FC<S, R> & {
306
306
  /**
307
307
  * The rendering context object.
308
308
  *