rip-lang 3.8.8 → 3.8.9

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.
Binary file
package/docs/dist/ui.js CHANGED
@@ -586,7 +586,7 @@ export const createRouter = (function(components, opts = {}) {
586
586
  tree = buildRoutes(components, root);
587
587
  navCallbacks = new Set();
588
588
  components.watch(function(event, path) {
589
- if (!(path.startsWith(root + '/'))) return;
589
+ if (!path.startsWith(root + '/')) return;
590
590
  return (tree = buildRoutes(components, root));
591
591
  });
592
592
  resolve = function(url) {
@@ -845,7 +845,13 @@ export const createRenderer = (function(opts = {}) {
845
845
  }
846
846
  }
847
847
  };
848
- return (!handled ? container.appendChild(pre) : undefined);
848
+ return (() => { if (!handled) {
849
+ pre = document.createElement('pre');
850
+ pre.style.cssText = 'color:red;padding:1em';
851
+ pre.textContent = err.stack || err.message;
852
+ container.innerHTML = '';
853
+ return container.appendChild(pre);
854
+ } })();
849
855
  } })();
850
856
  };
851
857
  renderer = {start: (function() {
@@ -890,7 +896,7 @@ export const launch = (async function(appBase = '', opts = {}) {
890
896
  } else {
891
897
  bundleUrl = `${appBase}/bundle`;
892
898
  res = await fetch(bundleUrl);
893
- if (!(res.ok)) {
899
+ if (!res.ok) {
894
900
  throw new Error(`launch: ${bundleUrl} (${res.status})`);
895
901
  };
896
902
  bundle = await res.json();
@@ -1,2 +1,2 @@
1
- var se,te,W,O,G,de,M,C,Z,I,H,ne,pe,he,F,J,me,ye,ge,B,ee,Ee,ve,re,Pe,N,oe,we,be,je,_e,xe,ie,Se;({__state:C,__effect:M,__batch:de}=globalThis.__rip);({setContext:je,getContext:Ee,hasContext:Pe}=globalThis.__ripComponent||{});G=Symbol("stash");O=Symbol("signals");W=Symbol("raw");te=new WeakMap;Z=0;ne=C(0);re=function(n,r){let o;if(!n[O])Object.defineProperty(n,O,{value:new Map,enumerable:!1});if(o=n[O].get(r),!o)o=C(n[r]),n[O].set(r,o);return o};N=function(n){return re(n,Symbol.for("keys"))};Se=function(n){let r;if(!(n!=null&&typeof n==="object"))return n;if(n[G])return n;if(n instanceof Date||n instanceof RegExp||n instanceof Map||n instanceof Set||n instanceof Promise)return n;if(r=te.get(n),r)return r;return oe(n)};oe=function(n){let r,o;return o=null,r={get:function(t,e){let i,u;if(e===G)return!0;if(e===W)return t;if(typeof e==="symbol")return Reflect.get(t,e);if(e==="length"&&Array.isArray(t))return N(t).value,t.length;if(e==="get")return function(a){return _e(o,a)};if(e==="set")return function(a,f){return xe(o,a,f)};if(i=re(t,e),u=i.value,u!=null&&typeof u==="object")return Se(u);return u},set:function(t,e,i){let u,a;if(u=t[e],a=i?.[W]?i[W]:i,a===u)return!0;if(t[e]=a,t[O]?.has(e))t[O].get(e).value=a;if(u===void 0&&a!==void 0)N(t).value=++Z;return ne.value++,!0},deleteProperty:function(t,e){let i;if(delete t[e],i=t[O]?.get(e),i)i.value=void 0;return N(t).value=++Z,!0},ownKeys:function(t){return N(t).value,Reflect.ownKeys(t)}},o=new Proxy(n,r),te.set(n,o),o};se=/([./][^./\[\s]+|\[[-+]?\d+\]|\[(?:"[^"]+"|'[^']+')\])/;ie=function(n){let r,o,t,e,i;t=("."+n).split(se),t.shift(),i=[],o=0;while(o<t.length){if(e=t[o],r=e[0],r==="."||r==="/")i.push(e.slice(1));else if(r==="[")if(e[1]==='"'||e[1]==="'")i.push(e.slice(2,-2));else i.push(+e.slice(1,-1));o+=2}return i};_e=function(n,r){let o,t;t=ie(r),o=n;for(let e of t){if(o==null)return;o=o[e]}return o};xe=function(n,r,o){let t,e;e=ie(r),t=n;for(let i=0;i<e.length;i++){let u=e[i];if(i===e.length-1)t[u]=o;else{if(t[u]==null)t[u]={};t=t[u]}}return o};H=function(n){return typeof n==="function"?n:function(){return n.value}};I=function(n,r){let o;return o={read:function(){return n.read()}},Object.defineProperty(o,"value",{get:function(){return n.value},set:function(t){return r.value=t}}),o};ge=function(n){let r;if(r=n.replace(/\.rip$/,""),r=r.replace(/\[\.\.\.(\w+)\]/g,"*$1"),r=r.replace(/\[(\w+)\]/g,":$1"),r==="index")return"/";return r=r.replace(/\/index$/,""),"/"+r};be=function(n){let r,o;return r=[],o=n.replace(/\*(\w+)/g,function(t,e){return r.push(e),"(.+)"}).replace(/:(\w+)/g,function(t,e){return r.push(e),"([^/]+)"}),{regex:new RegExp("^"+o+"$"),names:r}};we=function(n,r){let o,t;for(let e of r)if(o=n.match(e.regex.regex),o){t={};for(let i=0;i<e.regex.names.length;i++){let u=e.regex.names[i];t[u]=decodeURIComponent(o[i+1])}return{route:e,params:t}}return null};F=function(n,r="components"){let o,t,e,i,u,a,f,m;f=[],e=new Map,o=n.listAll(r);for(let c of o){if(a=c.slice(r.length+1),!a.endsWith(".rip"))continue;if(i=a.split("/").pop(),i==="_layout.rip"){t=a==="_layout.rip"?"":a.slice(0,-12),e.set(t,c);continue}if(i.startsWith("_"))continue;m=ge(a),u=be(m),f.push({pattern:m,regex:u,file:c,rel:a})}return f.sort(function(c,g){let p,x,y,d;if(x=(c.pattern.match(/:/g)||[]).length,d=(g.pattern.match(/:/g)||[]).length,p=c.pattern.includes("*")?1:0,y=g.pattern.includes("*")?1:0,p!==y)return p-y;if(x!==d)return x-d;return c.pattern.localeCompare(g.pattern)}),{routes:f,layouts:e}};ve=function(n,r,o){let t,e,i,u;if(t=[],i=n.slice(r.length+1),u=i.split("/"),e="",o.has(""))t.push(o.get(""));for(let a=0;a<u.length;a++){let f=u[a];if(a===u.length-1)break;if(e=e?e+"/"+f:f,o.has(e))t.push(o.get(e))}return t};pe=function(n,r){if(n.length!==r.length)return!1;for(let o=0;o<n.length;o++)if(n[o]!==r[o])return!1;return!0};ee=function(n){for(let r in n){let o=n[r];if(typeof o==="function"&&(o.prototype?.mount||o.prototype?._create))return o}return typeof n.default==="function"?n.default:void 0};B=function(n){let r;r={};for(let o in n){let t=n[o];if(typeof t==="function"&&(t.prototype?.mount||t.prototype?._create))r[o]=t}return r};ye=function(n){let r;return r=n.split("/").pop().replace(/\.rip$/,""),r.replace(/(^|[-_])([a-z])/g,function(o,t,e){return e.toUpperCase()})};he=function(n,r="components"){let o,t,e;t={};for(let i of n.listAll(r)){if(!i.endsWith(".rip"))continue;if(o=i.split("/").pop(),o.startsWith("_"))continue;if(e=ye(i),t[e])console.warn(`[Rip] Component name collision: ${e} (${t[e]} vs ${i})`);t[e]=i}return t};J=async function(n,r,o=null,t=null,e=null){let i,u,a,f,m,c,g,p,x,y,d;if(o&&t){if(u=o.getCompiled(t),u)return u}if(c=r(n),e){x={};for(let h in e.map){let v=e.map[h];if(v!==t&&c.includes(`new ${h}(`)){if(!e.classes[h]){if(f=o.read(v),f){a=await J(f,r,o,v,e),m=B(a);for(let S in m){let s=m[S];e.classes[S]=s}}}if(e.classes[h])x[h]=!0}}if(p=Object.keys(x),p.length>0)y=`const {${p.join(", ")}} = globalThis['${e.key}'];
2
- `,c=y+c}i=new Blob([c],{type:"application/javascript"}),d=URL.createObjectURL(i);try{g=await import(d)}finally{URL.revokeObjectURL(d)}if(e){m=B(g);for(let h in m){let v=m[h];e.classes[h]=v}}if(o&&t)o.setCompiled(t,g);return g};me=function(n,r,o,t,e=""){let i,u,a;return a=1000,u=30000,i=function(){let f;return f=new EventSource(t),f.addEventListener("connected",function(){return a=1000,console.log("[Rip] Hot reload connected")}),f.addEventListener("changed",async function(m){let c,g,p,x,y;({paths:p}=JSON.parse(m.data));for(let d of p)n.del(d);if(r.rebuild(),c=r.current,y=p.filter(function(d){return d===c.route?.file||c.layouts?.includes(d)}),y.length>0){x=await Promise.allSettled(y.map(async function(d){let h,v;return v=await fetch(e+"/"+d),h=await v.text(),n.write(d,h)})),g=x.filter(function(d){return d.status==="rejected"});for(let d of g)console.error("[Rip] Hot reload fetch error:",d.reason);return o.remount()}}),f.onerror=function(){return f.close(),console.log(`[Rip] Hot reload reconnecting in ${a/1000}s...`),setTimeout(i,a),a=Math.min(a*2,u)}},i()};var ke=function(n={}){return oe(n)},Te=function(n){return n?.[W]?n[W]:n},Ae=function(n){return n?.[G]===!0},Ue=function(n,r={}){let o,t,e,i,u;if(o=C(r.initial||null),e=C(!1),t=C(null),i=async function(){let a;return e.value=!0,t.value=null,(async()=>{try{return a=await n(),o.value=a}catch(f){return t.value=f}finally{e.value=!1}})()},u={data:void 0,loading:void 0,error:void 0,refetch:i},Object.defineProperty(u,"data",{get:function(){return o.value}}),Object.defineProperty(u,"loading",{get:function(){return e.value}}),Object.defineProperty(u,"error",{get:function(){return t.value}}),!r.lazy)i();return u},$e=function(n,r){let o,t;return o=H(r),t=C(!!o()),M(function(){let e;if(o())return e=setTimeout(function(){return t.value=!0},n),function(){return clearTimeout(e)};else return t.value=!1}),typeof r!=="function"?I(t,r):t},We=function(n,r){let o,t;return o=H(r),t=C(o()),M(function(){let e,i;return i=o(),e=setTimeout(function(){return t.value=i},n),function(){return clearTimeout(e)}}),typeof r!=="function"?I(t,r):t},qe=function(n,r){let o,t,e;return o=H(r),e=C(o()),t=0,M(function(){let i,u,a,f;if(f=o(),i=Date.now(),u=n-(i-t),u<=0)return e.value=f,t=i;else return a=setTimeout(function(){return e.value=o(),t=Date.now()},u),function(){return clearTimeout(a)}}),typeof r!=="function"?I(e,r):e},De=function(n,r){let o,t;return o=H(r),t=C(!!o()),M(function(){let e;if(o())return t.value=!0;else return e=setTimeout(function(){return t.value=!1},n),function(){return clearTimeout(e)}}),typeof r!=="function"?I(t,r):t},Oe=function(){let n,r,o,t;return r=new Map,t=[],n=new Map,o=function(e,i){for(let u of t)u(e,i)},{read:function(e){return r.get(e)},write:function(e,i){let u;return u=!r.has(e),r.set(e,i),n.delete(e),o(u?"create":"change",e)},del:function(e){return r.delete(e),n.delete(e),o("delete",e)},exists:function(e){return r.has(e)},size:function(){return r.size},list:function(e=""){let i,u,a;a=[],i=e?e+"/":"";for(let[f]of r)if(f.startsWith(i)){if(u=f.slice(i.length),u.includes("/"))continue;a.push(f)}return a},listAll:function(e=""){let i,u;u=[],i=e?e+"/":"";for(let[a]of r)if(a.startsWith(i))u.push(a);return u},load:function(e){for(let i in e){let u=e[i];r.set(i,u)}},watch:function(e){return t.push(e),function(){return t.splice(t.indexOf(e),1)}},getCompiled:function(e){return n.get(e)},setCompiled:function(e,i){return n.set(e,i)}}},Le=function(n,r={}){let o,t,e,i,u,a,f,m,c,g,p,x,y,d,h,v,S,s,P,b,w;if(S=r.root||"components",c=r.base||"",g=r.hash||!1,y=r.onError||null,P=function(l){return c&&l.startsWith(c)?l.slice(c.length)||"/":l},m=function(l){return c?c+l:l},h=function(){let l;if(g){if(l=location.hash.slice(1),!l)return"/";return l[0]==="/"?l:"/"+l}else return location.pathname+location.search+location.hash},w=function(l){return g?l==="/"?location.pathname:"#"+l.slice(1):m(l)},u=C(P(g?h():location.pathname)),i=C({}),f=C(null),t=C([]),a=C({}),o=C(""),e=$e(100,C(!1)),b=F(n,S),p=new Set,n.watch(function(l,R){if(!R.startsWith(S+"/"))return;return b=F(n,S)}),v=function(l){let R,_,E,A,k;if(A=l.split("?")[0].split("#")[0],_=P(A),_=_[0]==="/"?_:"/"+_,E=l.split("?")[1]?.split("#")[0]||"",R=l.includes("#")?l.split("#")[1]:"",k=we(_,b.routes),k){de(function(){return u.value=_,i.value=k.params,f.value=k.route,t.value=ve(k.route.file,S,b.layouts),a.value=Object.fromEntries(new URLSearchParams(E)),o.value=R});for(let T of p)T(s.current);return!0}if(y)y({status:404,path:_});return!1},d=function(){return v(h())},typeof window<"u")window.addEventListener("popstate",d);if(x=function(l){let R,_,E;if(l.button!==0||l.metaKey||l.ctrlKey||l.shiftKey||l.altKey)return;_=l.target;while(_&&_.tagName!=="A")_=_.parentElement;if(!_?.href)return;if(E=new URL(_.href,location.origin),E.origin!==location.origin)return;if(_.target==="_blank"||_.hasAttribute("data-external"))return;return l.preventDefault(),R=g&&E.hash?E.hash.slice(1)||"/":E.pathname+E.search+E.hash,s.push(R)},typeof document<"u")document.addEventListener("click",x);return s={push:function(l){return v(l)?history.pushState(null,"",w(u.read())):void 0},replace:function(l){return v(l)?history.replaceState(null,"",w(u.read())):void 0},back:function(){return history.back()},forward:function(){return history.forward()},current:void 0,path:void 0,params:void 0,route:void 0,layouts:void 0,query:void 0,hash:void 0,navigating:void 0,onNavigate:function(l){return p.add(l),function(){return p.delete(l)}},rebuild:function(){return b=F(n,S)},routes:void 0,init:function(){return v(h()),s},destroy:function(){if(typeof window<"u")window.removeEventListener("popstate",d);if(typeof document<"u")document.removeEventListener("click",x);return p.clear()}},Object.defineProperty(s,"current",{get:function(){return{path:u.value,params:i.value,route:f.value,layouts:t.value,query:a.value,hash:o.value}}}),Object.defineProperty(s,"path",{get:function(){return u.value}}),Object.defineProperty(s,"params",{get:function(){return i.value}}),Object.defineProperty(s,"route",{get:function(){return f.value}}),Object.defineProperty(s,"layouts",{get:function(){return t.value}}),Object.defineProperty(s,"query",{get:function(){return a.value}}),Object.defineProperty(s,"hash",{get:function(){return o.value}}),Object.defineProperty(s,"navigating",{get:function(){return e.value},set:function(l){return e.value=l}}),Object.defineProperty(s,"routes",{get:function(){return b.routes}}),s},Me=function(n={}){let r,o,t,e,i,u,a,f,m,c,g,p,x,y,d,h,v,S,s,P,b;if({router:s,app:r,components:i,resolver:S,compile:t,target:P,onError:h}=n,u=typeof P==="string"?document.querySelector(P):P||document.getElementById("app"),!u)u=document.createElement("div"),u.id="app",document.body.appendChild(u);return u.style.opacity="0",a=null,m=null,f=[],p=[],y=u,g=0,c=null,e=new Map,x=n.cacheSize||10,o=function(){let w,l;if(a&&m){if(a.beforeUnmount)a.beforeUnmount();if(e.set(m,a),e.size>x){if(l=e.keys().next().value,w=e.get(l),w.unmounted)w.unmounted();e.delete(l)}return a=null,m=null}},b=function(){o();for(let w=p.length-1;w>=0;w--){let l=p[w];if(l.beforeUnmount)l.beforeUnmount();if(l.unmounted)l.unmounted();l._root?.remove()}return p=[],y=u},i.watch(function(w,l){let R;if(e.has(l)){if(R=e.get(l),R.unmounted)R.unmounted();return e.delete(l)}}),d=async function(w){let l,R,_,E,A,k,T,q,ue,V,D,ae,L,le,K,z,Ce,X,j,fe,Q,U;if({route:j,params:z,layouts:q,query:X}=w,!j)return;if(j.file===m)return;return E=++g,s.navigating=!0,(async()=>{try{if(Q=i.read(j.file),!Q){if(h)h({status:404,message:`File not found: ${j.file}`});s.navigating=!1;return}if(ae=await J(Q,t,i,j.file,S),E!==g){s.navigating=!1;return}if(l=ee(ae),!l){if(h)h({status:500,message:`No component found in ${j.file}`});s.navigating=!1;return}if(D=!pe(q,f),le=a?._root,D)b();else o();if(L=D?u:y,D&&q.length>0){u.innerHTML="",L=u;for(let $ of q){if(V=i.read($),!V)continue;if(ue=await J(V,t,i,$,S),E!==g){s.navigating=!1;return}if(R=ee(ue),!R)continue;if(k=new R({app:r,params:z,router:s}),k.beforeMount)k.beforeMount();U=document.createElement("div"),U.setAttribute("data-layout",$),L.appendChild(U),k.mount(U),p.push(k),fe=U.querySelector("#content")||U,L=fe}f=[...q],y=L}else if(D)u.innerHTML="",f=[],y=u;if(_=e.get(j.file),_)e.delete(j.file),L.appendChild(_._root),a=_,m=j.file;else{if(K=document.createElement("div"),K.setAttribute("data-component",j.file),L.appendChild(K),T=new l({app:r,params:z,query:X,router:s}),T.beforeMount)T.beforeMount();if(T.mount(K),a=T,m=j.file,T.load)await T.load(z,X)}return le?.remove(),s.navigating=!1,u.style.opacity==="0"?document.fonts.ready.then(function(){return requestAnimationFrame(function(){return u.style.transition="opacity 150ms ease-in",u.style.opacity="1"})}):void 0}catch($){if(s.navigating=!1,u.style.opacity="1",console.error(`Renderer: error mounting ${j.file}:`,$),h)h({status:500,message:$.message,error:$});A=!1;for(let Y=p.length-1;Y>=0;Y--){let ce=p[Y];if(ce.onError)try{ce.onError($),A=!0;break}catch(Re){console.error("Renderer: error boundary failed:",Re)}}return!A?u.appendChild(Ce):void 0}})()},v={start:function(){return c=M(function(){let w;return w=s.current,w.route?d(w):void 0}),s.init(),v},stop:function(){if(b(),c)c(),c=null;return u.innerHTML=""},remount:function(){let w;return w=s.current,w.route?d(w):void 0},cache:e},v},Ne=async function(n="",r={}){let o,t,e,i,u,a,f,m,c,g,p,x,y,d,h,v,S,s,P;if(n=n.replace(/\/+$/,""),P=r.target||"#app",c=r.compile||null,x=r.persist||!1,p=r.hash||!1,!c)c=globalThis?.compileToJS||null;if(typeof document<"u"&&!document.querySelector(P))g=document.createElement("div"),g.id=P.replace(/^#/,""),document.body.prepend(g);if(r.bundle)a=r.bundle;else{if(f=`${n}/bundle`,d=await fetch(f),!d.ok)throw Error(`launch: ${f} (${d.status})`);a=await d.json()}if(i=ke({components:{},routes:{},data:{}}),a.data)i.data=a.data;if(a.routes)i.routes=a.routes;if(x&&typeof sessionStorage<"u"){e=`__rip_${n}`,t=x==="local"?localStorage:sessionStorage;try{if(S=t.getItem(e),S){s=JSON.parse(S);for(let b in s){let w=s[b];i.data[b]=w}}}catch{}o=function(){return(()=>{try{return t.setItem(e,JSON.stringify(Te(i.data)))}catch{return null}})()},M(function(){let b;return ne.value,b=setTimeout(o,2000),function(){return clearTimeout(b)}}),window.addEventListener("beforeunload",o)}if(u=Oe(),a.components)u.load(a.components);if(m=`__rip_${n.replace(/\//g,"_")||"app"}`,h={map:he(u),classes:{},key:m},typeof globalThis<"u")globalThis[m]=h.classes;if(i.data.title&&typeof document<"u")document.title=i.data.title;if(v=Le(u,{root:"components",base:n,hash:p,onError:function(b){return console.error(`[Rip] Error ${b.status}: ${b.message||b.path}`)}}),y=Me({router:v,app:i,components:u,resolver:h,compile:c,target:P,onError:function(b){return console.error(`[Rip] ${b.message}`,b.error)}}),y.start(),a.data?.watch)me(u,v,y,`${n}/watch`,n);if(typeof window<"u")window.app=i,window.__RIP__={app:i,components:u,router:v,renderer:y,cache:y.cache,version:"0.3.0"};return{app:i,components:u,router:v,renderer:y}};export{qe as throttle,ke as stash,je as setContext,Te as raw,Ne as launch,Ae as isStash,De as hold,Pe as hasContext,Ee as getContext,$e as delay,We as debounce,Le as createRouter,Ue as createResource,Me as createRenderer,Oe as createComponents};
1
+ var de,ne,W,O,V,pe,M,S,B,H,I,re,he,me,J,G,ye,ge,ve,ee,te,Re,we,oe,Pe,N,ie,be,_e,ke,xe,Ce,ue,Se;({__state:S,__effect:M,__batch:pe}=globalThis.__rip);({setContext:ke,getContext:Re,hasContext:Pe}=globalThis.__ripComponent||{});V=Symbol("stash");O=Symbol("signals");W=Symbol("raw");ne=new WeakMap;B=0;re=S(0);oe=function(n,r){let o;if(!n[O])Object.defineProperty(n,O,{value:new Map,enumerable:!1});if(o=n[O].get(r),!o)o=S(n[r]),n[O].set(r,o);return o};N=function(n){return oe(n,Symbol.for("keys"))};Se=function(n){let r;if(!(n!=null&&typeof n==="object"))return n;if(n[V])return n;if(n instanceof Date||n instanceof RegExp||n instanceof Map||n instanceof Set||n instanceof Promise)return n;if(r=ne.get(n),r)return r;return ie(n)};ie=function(n){let r,o;return o=null,r={get:function(t,e){let i,u;if(e===V)return!0;if(e===W)return t;if(typeof e==="symbol")return Reflect.get(t,e);if(e==="length"&&Array.isArray(t))return N(t).value,t.length;if(e==="get")return function(a){return xe(o,a)};if(e==="set")return function(a,f){return Ce(o,a,f)};if(i=oe(t,e),u=i.value,u!=null&&typeof u==="object")return Se(u);return u},set:function(t,e,i){let u,a;if(u=t[e],a=i?.[W]?i[W]:i,a===u)return!0;if(t[e]=a,t[O]?.has(e))t[O].get(e).value=a;if(u===void 0&&a!==void 0)N(t).value=++B;return re.value++,!0},deleteProperty:function(t,e){let i;if(delete t[e],i=t[O]?.get(e),i)i.value=void 0;return N(t).value=++B,!0},ownKeys:function(t){return N(t).value,Reflect.ownKeys(t)}},o=new Proxy(n,r),ne.set(n,o),o};de=/([./][^./\[\s]+|\[[-+]?\d+\]|\[(?:"[^"]+"|'[^']+')\])/;ue=function(n){let r,o,t,e,i;t=("."+n).split(de),t.shift(),i=[],o=0;while(o<t.length){if(e=t[o],r=e[0],r==="."||r==="/")i.push(e.slice(1));else if(r==="[")if(e[1]==='"'||e[1]==="'")i.push(e.slice(2,-2));else i.push(+e.slice(1,-1));o+=2}return i};xe=function(n,r){let o,t;t=ue(r),o=n;for(let e of t){if(o==null)return;o=o[e]}return o};Ce=function(n,r,o){let t,e;e=ue(r),t=n;for(let i=0;i<e.length;i++){let u=e[i];if(i===e.length-1)t[u]=o;else{if(t[u]==null)t[u]={};t=t[u]}}return o};I=function(n){return typeof n==="function"?n:function(){return n.value}};H=function(n,r){let o;return o={read:function(){return n.read()}},Object.defineProperty(o,"value",{get:function(){return n.value},set:function(t){return r.value=t}}),o};ve=function(n){let r;if(r=n.replace(/\.rip$/,""),r=r.replace(/\[\.\.\.(\w+)\]/g,"*$1"),r=r.replace(/\[(\w+)\]/g,":$1"),r==="index")return"/";return r=r.replace(/\/index$/,""),"/"+r};_e=function(n){let r,o;return r=[],o=n.replace(/\*(\w+)/g,function(t,e){return r.push(e),"(.+)"}).replace(/:(\w+)/g,function(t,e){return r.push(e),"([^/]+)"}),{regex:new RegExp("^"+o+"$"),names:r}};be=function(n,r){let o,t;for(let e of r)if(o=n.match(e.regex.regex),o){t={};for(let i=0;i<e.regex.names.length;i++){let u=e.regex.names[i];t[u]=decodeURIComponent(o[i+1])}return{route:e,params:t}}return null};J=function(n,r="components"){let o,t,e,i,u,a,f,m;f=[],e=new Map,o=n.listAll(r);for(let c of o){if(a=c.slice(r.length+1),!a.endsWith(".rip"))continue;if(i=a.split("/").pop(),i==="_layout.rip"){t=a==="_layout.rip"?"":a.slice(0,-12),e.set(t,c);continue}if(i.startsWith("_"))continue;m=ve(a),u=_e(m),f.push({pattern:m,regex:u,file:c,rel:a})}return f.sort(function(c,g){let p,x,y,d;if(x=(c.pattern.match(/:/g)||[]).length,d=(g.pattern.match(/:/g)||[]).length,p=c.pattern.includes("*")?1:0,y=g.pattern.includes("*")?1:0,p!==y)return p-y;if(x!==d)return x-d;return c.pattern.localeCompare(g.pattern)}),{routes:f,layouts:e}};we=function(n,r,o){let t,e,i,u;if(t=[],i=n.slice(r.length+1),u=i.split("/"),e="",o.has(""))t.push(o.get(""));for(let a=0;a<u.length;a++){let f=u[a];if(a===u.length-1)break;if(e=e?e+"/"+f:f,o.has(e))t.push(o.get(e))}return t};he=function(n,r){if(n.length!==r.length)return!1;for(let o=0;o<n.length;o++)if(n[o]!==r[o])return!1;return!0};te=function(n){for(let r in n){let o=n[r];if(typeof o==="function"&&(o.prototype?.mount||o.prototype?._create))return o}return typeof n.default==="function"?n.default:void 0};ee=function(n){let r;r={};for(let o in n){let t=n[o];if(typeof t==="function"&&(t.prototype?.mount||t.prototype?._create))r[o]=t}return r};ge=function(n){let r;return r=n.split("/").pop().replace(/\.rip$/,""),r.replace(/(^|[-_])([a-z])/g,function(o,t,e){return e.toUpperCase()})};me=function(n,r="components"){let o,t,e;t={};for(let i of n.listAll(r)){if(!i.endsWith(".rip"))continue;if(o=i.split("/").pop(),o.startsWith("_"))continue;if(e=ge(i),t[e])console.warn(`[Rip] Component name collision: ${e} (${t[e]} vs ${i})`);t[e]=i}return t};G=async function(n,r,o=null,t=null,e=null){let i,u,a,f,m,c,g,p,x,y,d;if(o&&t){if(u=o.getCompiled(t),u)return u}if(c=r(n),e){x={};for(let h in e.map){let v=e.map[h];if(v!==t&&c.includes(`new ${h}(`)){if(!e.classes[h]){if(f=o.read(v),f){a=await G(f,r,o,v,e),m=ee(a);for(let C in m){let s=m[C];e.classes[C]=s}}}if(e.classes[h])x[h]=!0}}if(p=Object.keys(x),p.length>0)y=`const {${p.join(", ")}} = globalThis['${e.key}'];
2
+ `,c=y+c}i=new Blob([c],{type:"application/javascript"}),d=URL.createObjectURL(i);try{g=await import(d)}finally{URL.revokeObjectURL(d)}if(e){m=ee(g);for(let h in m){let v=m[h];e.classes[h]=v}}if(o&&t)o.setCompiled(t,g);return g};ye=function(n,r,o,t,e=""){let i,u,a;return a=1000,u=30000,i=function(){let f;return f=new EventSource(t),f.addEventListener("connected",function(){return a=1000,console.log("[Rip] Hot reload connected")}),f.addEventListener("changed",async function(m){let c,g,p,x,y;({paths:p}=JSON.parse(m.data));for(let d of p)n.del(d);if(r.rebuild(),c=r.current,y=p.filter(function(d){return d===c.route?.file||c.layouts?.includes(d)}),y.length>0){x=await Promise.allSettled(y.map(async function(d){let h,v;return v=await fetch(e+"/"+d),h=await v.text(),n.write(d,h)})),g=x.filter(function(d){return d.status==="rejected"});for(let d of g)console.error("[Rip] Hot reload fetch error:",d.reason);return o.remount()}}),f.onerror=function(){return f.close(),console.log(`[Rip] Hot reload reconnecting in ${a/1000}s...`),setTimeout(i,a),a=Math.min(a*2,u)}},i()};var Te=function(n={}){return ie(n)},je=function(n){return n?.[W]?n[W]:n},Ae=function(n){return n?.[V]===!0},Ue=function(n,r={}){let o,t,e,i,u;if(o=S(r.initial||null),e=S(!1),t=S(null),i=async function(){let a;return e.value=!0,t.value=null,(async()=>{try{return a=await n(),o.value=a}catch(f){return t.value=f}finally{e.value=!1}})()},u={data:void 0,loading:void 0,error:void 0,refetch:i},Object.defineProperty(u,"data",{get:function(){return o.value}}),Object.defineProperty(u,"loading",{get:function(){return e.value}}),Object.defineProperty(u,"error",{get:function(){return t.value}}),!r.lazy)i();return u},$e=function(n,r){let o,t;return o=I(r),t=S(!!o()),M(function(){let e;if(o())return e=setTimeout(function(){return t.value=!0},n),function(){return clearTimeout(e)};else return t.value=!1}),typeof r!=="function"?H(t,r):t},We=function(n,r){let o,t;return o=I(r),t=S(o()),M(function(){let e,i;return i=o(),e=setTimeout(function(){return t.value=i},n),function(){return clearTimeout(e)}}),typeof r!=="function"?H(t,r):t},qe=function(n,r){let o,t,e;return o=I(r),e=S(o()),t=0,M(function(){let i,u,a,f;if(f=o(),i=Date.now(),u=n-(i-t),u<=0)return e.value=f,t=i;else return a=setTimeout(function(){return e.value=o(),t=Date.now()},u),function(){return clearTimeout(a)}}),typeof r!=="function"?H(e,r):e},De=function(n,r){let o,t;return o=I(r),t=S(!!o()),M(function(){let e;if(o())return t.value=!0;else return e=setTimeout(function(){return t.value=!1},n),function(){return clearTimeout(e)}}),typeof r!=="function"?H(t,r):t},Oe=function(){let n,r,o,t;return r=new Map,t=[],n=new Map,o=function(e,i){for(let u of t)u(e,i)},{read:function(e){return r.get(e)},write:function(e,i){let u;return u=!r.has(e),r.set(e,i),n.delete(e),o(u?"create":"change",e)},del:function(e){return r.delete(e),n.delete(e),o("delete",e)},exists:function(e){return r.has(e)},size:function(){return r.size},list:function(e=""){let i,u,a;a=[],i=e?e+"/":"";for(let[f]of r)if(f.startsWith(i)){if(u=f.slice(i.length),u.includes("/"))continue;a.push(f)}return a},listAll:function(e=""){let i,u;u=[],i=e?e+"/":"";for(let[a]of r)if(a.startsWith(i))u.push(a);return u},load:function(e){for(let i in e){let u=e[i];r.set(i,u)}},watch:function(e){return t.push(e),function(){return t.splice(t.indexOf(e),1)}},getCompiled:function(e){return n.get(e)},setCompiled:function(e,i){return n.set(e,i)}}},Le=function(n,r={}){let o,t,e,i,u,a,f,m,c,g,p,x,y,d,h,v,C,s,P,b,w;if(C=r.root||"components",c=r.base||"",g=r.hash||!1,y=r.onError||null,P=function(l){return c&&l.startsWith(c)?l.slice(c.length)||"/":l},m=function(l){return c?c+l:l},h=function(){let l;if(g){if(l=location.hash.slice(1),!l)return"/";return l[0]==="/"?l:"/"+l}else return location.pathname+location.search+location.hash},w=function(l){return g?l==="/"?location.pathname:"#"+l.slice(1):m(l)},u=S(P(g?h():location.pathname)),i=S({}),f=S(null),t=S([]),a=S({}),o=S(""),e=$e(100,S(!1)),b=J(n,C),p=new Set,n.watch(function(l,E){if(!E.startsWith(C+"/"))return;return b=J(n,C)}),v=function(l){let E,_,R,A,T;if(A=l.split("?")[0].split("#")[0],_=P(A),_=_[0]==="/"?_:"/"+_,R=l.split("?")[1]?.split("#")[0]||"",E=l.includes("#")?l.split("#")[1]:"",T=be(_,b.routes),T){pe(function(){return u.value=_,i.value=T.params,f.value=T.route,t.value=we(T.route.file,C,b.layouts),a.value=Object.fromEntries(new URLSearchParams(R)),o.value=E});for(let $ of p)$(s.current);return!0}if(y)y({status:404,path:_});return!1},d=function(){return v(h())},typeof window<"u")window.addEventListener("popstate",d);if(x=function(l){let E,_,R;if(l.button!==0||l.metaKey||l.ctrlKey||l.shiftKey||l.altKey)return;_=l.target;while(_&&_.tagName!=="A")_=_.parentElement;if(!_?.href)return;if(R=new URL(_.href,location.origin),R.origin!==location.origin)return;if(_.target==="_blank"||_.hasAttribute("data-external"))return;return l.preventDefault(),E=g&&R.hash?R.hash.slice(1)||"/":R.pathname+R.search+R.hash,s.push(E)},typeof document<"u")document.addEventListener("click",x);return s={push:function(l){return v(l)?history.pushState(null,"",w(u.read())):void 0},replace:function(l){return v(l)?history.replaceState(null,"",w(u.read())):void 0},back:function(){return history.back()},forward:function(){return history.forward()},current:void 0,path:void 0,params:void 0,route:void 0,layouts:void 0,query:void 0,hash:void 0,navigating:void 0,onNavigate:function(l){return p.add(l),function(){return p.delete(l)}},rebuild:function(){return b=J(n,C)},routes:void 0,init:function(){return v(h()),s},destroy:function(){if(typeof window<"u")window.removeEventListener("popstate",d);if(typeof document<"u")document.removeEventListener("click",x);return p.clear()}},Object.defineProperty(s,"current",{get:function(){return{path:u.value,params:i.value,route:f.value,layouts:t.value,query:a.value,hash:o.value}}}),Object.defineProperty(s,"path",{get:function(){return u.value}}),Object.defineProperty(s,"params",{get:function(){return i.value}}),Object.defineProperty(s,"route",{get:function(){return f.value}}),Object.defineProperty(s,"layouts",{get:function(){return t.value}}),Object.defineProperty(s,"query",{get:function(){return a.value}}),Object.defineProperty(s,"hash",{get:function(){return o.value}}),Object.defineProperty(s,"navigating",{get:function(){return e.value},set:function(l){return e.value=l}}),Object.defineProperty(s,"routes",{get:function(){return b.routes}}),s},Me=function(n={}){let r,o,t,e,i,u,a,f,m,c,g,p,x,y,d,h,v,C,s,P,b;if({router:s,app:r,components:i,resolver:C,compile:t,target:P,onError:h}=n,u=typeof P==="string"?document.querySelector(P):P||document.getElementById("app"),!u)u=document.createElement("div"),u.id="app",document.body.appendChild(u);return u.style.opacity="0",a=null,m=null,f=[],p=[],y=u,g=0,c=null,e=new Map,x=n.cacheSize||10,o=function(){let w,l;if(a&&m){if(a.beforeUnmount)a.beforeUnmount();if(e.set(m,a),e.size>x){if(l=e.keys().next().value,w=e.get(l),w.unmounted)w.unmounted();e.delete(l)}return a=null,m=null}},b=function(){o();for(let w=p.length-1;w>=0;w--){let l=p[w];if(l.beforeUnmount)l.beforeUnmount();if(l.unmounted)l.unmounted();l._root?.remove()}return p=[],y=u},i.watch(function(w,l){let E;if(e.has(l)){if(E=e.get(l),E.unmounted)E.unmounted();return e.delete(l)}}),d=async function(w){let l,E,_,R,A,T,$,q,ae,X,D,le,L,fe,K,z,F,Q,k,ce,Y,U;if({route:k,params:z,layouts:q,query:Q}=w,!k)return;if(k.file===m)return;return R=++g,s.navigating=!0,(async()=>{try{if(Y=i.read(k.file),!Y){if(h)h({status:404,message:`File not found: ${k.file}`});s.navigating=!1;return}if(le=await G(Y,t,i,k.file,C),R!==g){s.navigating=!1;return}if(l=te(le),!l){if(h)h({status:500,message:`No component found in ${k.file}`});s.navigating=!1;return}if(D=!he(q,f),fe=a?._root,D)b();else o();if(L=D?u:y,D&&q.length>0){u.innerHTML="",L=u;for(let j of q){if(X=i.read(j),!X)continue;if(ae=await G(X,t,i,j,C),R!==g){s.navigating=!1;return}if(E=te(ae),!E)continue;if(T=new E({app:r,params:z,router:s}),T.beforeMount)T.beforeMount();U=document.createElement("div"),U.setAttribute("data-layout",j),L.appendChild(U),T.mount(U),p.push(T),ce=U.querySelector("#content")||U,L=ce}f=[...q],y=L}else if(D)u.innerHTML="",f=[],y=u;if(_=e.get(k.file),_)e.delete(k.file),L.appendChild(_._root),a=_,m=k.file;else{if(K=document.createElement("div"),K.setAttribute("data-component",k.file),L.appendChild(K),$=new l({app:r,params:z,query:Q,router:s}),$.beforeMount)$.beforeMount();if($.mount(K),a=$,m=k.file,$.load)await $.load(z,Q)}return fe?.remove(),s.navigating=!1,u.style.opacity==="0"?document.fonts.ready.then(function(){return requestAnimationFrame(function(){return u.style.transition="opacity 150ms ease-in",u.style.opacity="1"})}):void 0}catch(j){if(s.navigating=!1,u.style.opacity="1",console.error(`Renderer: error mounting ${k.file}:`,j),h)h({status:500,message:j.message,error:j});A=!1;for(let Z=p.length-1;Z>=0;Z--){let se=p[Z];if(se.onError)try{se.onError(j),A=!0;break}catch(Ee){console.error("Renderer: error boundary failed:",Ee)}}return(()=>{if(!A)return F=document.createElement("pre"),F.style.cssText="color:red;padding:1em",F.textContent=j.stack||j.message,u.innerHTML="",u.appendChild(F)})()}})()},v={start:function(){return c=M(function(){let w;return w=s.current,w.route?d(w):void 0}),s.init(),v},stop:function(){if(b(),c)c(),c=null;return u.innerHTML=""},remount:function(){let w;return w=s.current,w.route?d(w):void 0},cache:e},v},Ne=async function(n="",r={}){let o,t,e,i,u,a,f,m,c,g,p,x,y,d,h,v,C,s,P;if(n=n.replace(/\/+$/,""),P=r.target||"#app",c=r.compile||null,x=r.persist||!1,p=r.hash||!1,!c)c=globalThis?.compileToJS||null;if(typeof document<"u"&&!document.querySelector(P))g=document.createElement("div"),g.id=P.replace(/^#/,""),document.body.prepend(g);if(r.bundle)a=r.bundle;else{if(f=`${n}/bundle`,d=await fetch(f),!d.ok)throw Error(`launch: ${f} (${d.status})`);a=await d.json()}if(i=Te({components:{},routes:{},data:{}}),a.data)i.data=a.data;if(a.routes)i.routes=a.routes;if(x&&typeof sessionStorage<"u"){e=`__rip_${n}`,t=x==="local"?localStorage:sessionStorage;try{if(C=t.getItem(e),C){s=JSON.parse(C);for(let b in s){let w=s[b];i.data[b]=w}}}catch{}o=function(){return(()=>{try{return t.setItem(e,JSON.stringify(je(i.data)))}catch{return null}})()},M(function(){let b;return re.value,b=setTimeout(o,2000),function(){return clearTimeout(b)}}),window.addEventListener("beforeunload",o)}if(u=Oe(),a.components)u.load(a.components);if(m=`__rip_${n.replace(/\//g,"_")||"app"}`,h={map:me(u),classes:{},key:m},typeof globalThis<"u")globalThis[m]=h.classes;if(i.data.title&&typeof document<"u")document.title=i.data.title;if(v=Le(u,{root:"components",base:n,hash:p,onError:function(b){return console.error(`[Rip] Error ${b.status}: ${b.message||b.path}`)}}),y=Me({router:v,app:i,components:u,resolver:h,compile:c,target:P,onError:function(b){return console.error(`[Rip] ${b.message}`,b.error)}}),y.start(),a.data?.watch)ye(u,v,y,`${n}/watch`,n);if(typeof window<"u")window.app=i,window.__RIP__={app:i,components:u,router:v,renderer:y,cache:y.cache,version:"0.3.0"};return{app:i,components:u,router:v,renderer:y}};export{qe as throttle,Te as stash,ke as setContext,je as raw,Ne as launch,Ae as isStash,De as hold,Pe as hasContext,Re as getContext,$e as delay,We as debounce,Le as createRouter,Ue as createResource,Me as createRenderer,Oe as createComponents};
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rip-lang",
3
- "version": "3.8.8",
3
+ "version": "3.8.9",
4
4
  "description": "A modern language that compiles to JavaScript",
5
5
  "type": "module",
6
6
  "main": "src/compiler.js",
package/src/compiler.js CHANGED
@@ -148,6 +148,7 @@ export class CodeGenerator {
148
148
 
149
149
  // Special operators
150
150
  '%%': 'generateModulo',
151
+ '%%=': 'generateModuloAssign',
151
152
  '//': 'generateFloorDiv',
152
153
  '//=': 'generateFloorDivAssign',
153
154
  '..': 'generateRange',
@@ -213,12 +214,10 @@ export class CodeGenerator {
213
214
 
214
215
  // Control flow — complex
215
216
  'if': 'generateIf',
216
- 'unless': 'generateIf',
217
217
  'for-in': 'generateForIn',
218
218
  'for-of': 'generateForOf',
219
219
  'for-as': 'generateForAs',
220
220
  'while': 'generateWhile',
221
- 'until': 'generateUntil',
222
221
  'try': 'generateTry',
223
222
  'throw': 'generateThrow',
224
223
  'control': 'generateControl',
@@ -318,7 +317,7 @@ export class CodeGenerator {
318
317
 
319
318
  if (!trimmed || trimmed === '}' || trimmed === '});') continue;
320
319
  if (trimmed.startsWith('let ') || trimmed.startsWith('var ')) continue;
321
- if (trimmed.startsWith('const slice') || trimmed.startsWith('const modulo') || trimmed.startsWith('const toSearchable')) continue;
320
+ if (trimmed.startsWith('const slice') || trimmed.startsWith('const modulo') || trimmed.startsWith('const toMatchable')) continue;
322
321
  if (trimmed.startsWith('const {') && trimmed.includes('__')) continue;
323
322
  if (trimmed.startsWith('} else')) continue;
324
323
  if (trimmed.startsWith('//# source')) continue;
@@ -382,13 +381,6 @@ export class CodeGenerator {
382
381
  return;
383
382
  }
384
383
 
385
- if (head === 'unless') {
386
- let [condition, body] = rest;
387
- this.collectProgramVariables(condition);
388
- this.collectProgramVariables(body);
389
- return;
390
- }
391
-
392
384
  if (head === 'try') {
393
385
  this.collectProgramVariables(rest[0]);
394
386
  if (rest.length >= 2 && Array.isArray(rest[1]) && rest[1].length === 2 && rest[1][0] !== 'block') {
@@ -543,7 +535,7 @@ export class CodeGenerator {
543
535
  return `super.${this.currentMethodName}(${args})`;
544
536
  }
545
537
 
546
- // Postfix if/unless on single-arg call
538
+ // Postfix if on single-arg call
547
539
  if (context === 'statement' && rest.length === 1) {
548
540
  let cond = this.findPostfixConditional(rest[0]);
549
541
  if (cond) {
@@ -552,7 +544,7 @@ export class CodeGenerator {
552
544
  let condCode = this.generate(cond.condition, 'value');
553
545
  let valCode = this.generate(argWithout, 'value');
554
546
  let callStr = `${callee}(${valCode})`;
555
- return cond.type === 'unless' ? `if (!${condCode}) ${callStr}` : `if (${condCode}) ${callStr}`;
547
+ return `if (${condCode}) ${callStr}`;
556
548
  }
557
549
  }
558
550
 
@@ -565,7 +557,7 @@ export class CodeGenerator {
565
557
 
566
558
  // Statement sequence (comma operator)
567
559
  if (Array.isArray(head) && typeof head[0] === 'string') {
568
- let stmtOps = ['=', '+=', '-=', '*=', '/=', '%=', '**=', '&&=', '||=', '??=', 'if', 'unless', 'return', 'throw'];
560
+ let stmtOps = ['=', '+=', '-=', '*=', '/=', '%=', '**=', '&&=', '||=', '??=', 'if', 'return', 'throw'];
569
561
  if (stmtOps.includes(head[0])) {
570
562
  let exprs = sexpr.map(stmt => this.generate(stmt, 'value'));
571
563
  return `(${exprs.join(', ')})`;
@@ -583,7 +575,7 @@ export class CodeGenerator {
583
575
  return `new ${needsParens ? `(${ctorCode})` : ctorCode}(${args})`;
584
576
  }
585
577
 
586
- // Postfix if/unless on single-arg method call
578
+ // Postfix if on single-arg method call
587
579
  if (context === 'statement' && rest.length === 1) {
588
580
  let cond = this.findPostfixConditional(rest[0]);
589
581
  if (cond) {
@@ -592,7 +584,7 @@ export class CodeGenerator {
592
584
  let condCode = this.generate(cond.condition, 'value');
593
585
  let valCode = this.generate(argWithout, 'value');
594
586
  let callStr = `${calleeCode}(${valCode})`;
595
- return cond.type === 'unless' ? `if (!${condCode}) ${callStr}` : `if (${condCode}) ${callStr}`;
587
+ return `if (${condCode}) ${callStr}`;
596
588
  }
597
589
  }
598
590
 
@@ -636,7 +628,7 @@ export class CodeGenerator {
636
628
  }
637
629
 
638
630
  // Generate body first to detect needed helpers
639
- let blockStmts = ['def', 'class', 'if', 'unless', 'for-in', 'for-of', 'for-as', 'while', 'until', 'loop', 'switch', 'try'];
631
+ let blockStmts = ['def', 'class', 'if', 'for-in', 'for-of', 'for-as', 'while', 'loop', 'switch', 'try'];
640
632
  let statementsCode = other.map((stmt, index) => {
641
633
  let isSingle = other.length === 1 && imports.length === 0 && exports.length === 0;
642
634
  let isObj = this.is(stmt, 'object');
@@ -678,8 +670,8 @@ export class CodeGenerator {
678
670
  if (!skip) {
679
671
  if (this.helpers.has('slice')) { code += 'const slice = [].slice;\n'; needsBlank = true; }
680
672
  if (this.helpers.has('modulo')) { code += 'const modulo = (n, d) => { n = +n; d = +d; return (n % d + d) % d; };\n'; needsBlank = true; }
681
- if (this.helpers.has('toSearchable')) {
682
- code += 'const toSearchable = (v, allowNewlines) => {\n';
673
+ if (this.helpers.has('toMatchable')) {
674
+ code += 'const toMatchable = (v, allowNewlines) => {\n';
683
675
  code += ' if (typeof v === "string") return !allowNewlines && /[\\n\\r]/.test(v) ? null : v;\n';
684
676
  code += ' if (v == null) return "";\n';
685
677
  code += ' if (typeof v === "number" || typeof v === "bigint" || typeof v === "boolean") return String(v);\n';
@@ -779,6 +771,13 @@ export class CodeGenerator {
779
771
  return `modulo(${this.generate(left, 'value')}, ${this.generate(right, 'value')})`;
780
772
  }
781
773
 
774
+ generateModuloAssign(head, rest) {
775
+ let [target, value] = rest;
776
+ this.helpers.add('modulo');
777
+ let t = this.generate(target, 'value'), v = this.generate(value, 'value');
778
+ return `${t} = modulo(${t}, ${v})`;
779
+ }
780
+
782
781
  generateFloorDiv(head, rest) {
783
782
  let [left, right] = rest;
784
783
  return `Math.floor(${this.generate(left, 'value')} / ${this.generate(right, 'value')})`;
@@ -868,33 +867,29 @@ export class CodeGenerator {
868
867
  }
869
868
  }
870
869
 
871
- // Postfix if/unless on assignment with || operator
870
+ // Postfix if on assignment with || operator
872
871
  if (context === 'statement' && head === '=' && Array.isArray(value) &&
873
872
  (value[0] === '||' || value[0] === '&&') && value.length === 3) {
874
873
  let [binOp, left, right] = value;
875
- if ((this.is(right, 'unless') || this.is(right, 'if')) && right.length === 3) {
876
- let [condType, condition, wrappedValue] = right;
874
+ if (this.is(right, 'if') && right.length === 3) {
875
+ let [, condition, wrappedValue] = right;
877
876
  let unwrapped = Array.isArray(wrappedValue) && wrappedValue.length === 1 ? wrappedValue[0] : wrappedValue;
878
877
  let fullValue = [binOp, left, unwrapped];
879
878
  let t = this.generate(target, 'value'), c = this.generate(condition, 'value'), v = this.generate(fullValue, 'value');
880
- return condType === 'unless' ? `if (!${c}) ${t} = ${v}` : `if (${c}) ${t} = ${v}`;
879
+ return `if (${c}) ${t} = ${v}`;
881
880
  }
882
881
  }
883
882
 
884
- // Postfix if/unless on simple assignment
883
+ // Postfix if on simple assignment
885
884
  if (context === 'statement' && head === '=' && Array.isArray(value) && value.length === 3) {
886
885
  let [valHead, condition, actualValue] = value;
887
886
  let isPostfix = Array.isArray(actualValue) && actualValue.length === 1 &&
888
887
  (!Array.isArray(actualValue[0]) || actualValue[0][0] !== 'block');
889
- if ((valHead === 'unless' || valHead === 'if') && isPostfix) {
888
+ if (valHead === 'if' && isPostfix) {
890
889
  let unwrapped = Array.isArray(actualValue) && actualValue.length === 1 ? actualValue[0] : actualValue;
891
890
  let t = this.generate(target, 'value');
892
891
  let condCode = this.unwrapLogical(this.generate(condition, 'value'));
893
892
  let v = this.generate(unwrapped, 'value');
894
- if (valHead === 'unless') {
895
- if (condCode.includes(' ') || /[<>=&|]/.test(condCode)) condCode = `(${condCode})`;
896
- return `if (!${condCode}) ${t} = ${v}`;
897
- }
898
893
  return `if (${condCode}) ${t} = ${v}`;
899
894
  }
900
895
  }
@@ -951,12 +946,12 @@ export class CodeGenerator {
951
946
 
952
947
  generateRegexIndex(head, rest) {
953
948
  let [value, regex, captureIndex] = rest;
954
- this.helpers.add('toSearchable');
949
+ this.helpers.add('toMatchable');
955
950
  this.programVars.add('_');
956
951
  let v = this.generate(value, 'value'), r = this.generate(regex, 'value');
957
952
  let idx = captureIndex !== null ? this.generate(captureIndex, 'value') : '0';
958
953
  let allowNL = r.includes('/m') ? ', true' : '';
959
- return `(_ = toSearchable(${v}${allowNL}).match(${r})) && _[${idx}]`;
954
+ return `(_ = toMatchable(${v}${allowNL}).match(${r})) && _[${idx}]`;
960
955
  }
961
956
 
962
957
  generateIndexAccess(head, rest) {
@@ -1066,11 +1061,6 @@ export class CodeGenerator {
1066
1061
  let [expr] = rest;
1067
1062
  if (this.sideEffectOnly) return 'return';
1068
1063
 
1069
- if (this.is(expr, 'unless')) {
1070
- let [, condition, body] = expr;
1071
- let val = Array.isArray(body) && body.length === 1 ? body[0] : body;
1072
- return `if (!${this.generate(condition, 'value')}) return ${this.generate(val, 'value')}`;
1073
- }
1074
1064
  if (this.is(expr, 'if')) {
1075
1065
  let [, condition, body, ...elseParts] = expr;
1076
1066
  if (elseParts.length === 0) {
@@ -1078,11 +1068,10 @@ export class CodeGenerator {
1078
1068
  return `if (${this.generate(condition, 'value')}) return ${this.generate(val, 'value')}`;
1079
1069
  }
1080
1070
  }
1081
- if (this.is(expr, 'new') && Array.isArray(expr[1]) && expr[1][0] === 'unless') {
1082
- let [, unlessNode] = expr;
1083
- let [, condition, body] = unlessNode;
1071
+ if (this.is(expr, 'new') && Array.isArray(expr[1]) && expr[1][0] === 'if') {
1072
+ let [, condition, body] = expr[1];
1084
1073
  let val = Array.isArray(body) && body.length === 1 ? body[0] : body;
1085
- return `if (!${this.generate(condition, 'value')}) return ${this.generate(['new', val], 'value')}`;
1074
+ return `if (${this.generate(condition, 'value')}) return ${this.generate(['new', val], 'value')}`;
1086
1075
  }
1087
1076
  return `return ${this.generate(expr, 'value')}`;
1088
1077
  }
@@ -1200,16 +1189,6 @@ export class CodeGenerator {
1200
1189
  // ---------------------------------------------------------------------------
1201
1190
 
1202
1191
  generateIf(head, rest, context, sexpr) {
1203
- if (head === 'unless') {
1204
- let [condition, body] = rest;
1205
- if (Array.isArray(body) && body.length === 1 && (!Array.isArray(body[0]) || body[0][0] !== 'block')) body = body[0];
1206
- if (context === 'value') {
1207
- return `(!${this.generate(condition, 'value')} ? ${this.extractExpression(body)} : undefined)`;
1208
- }
1209
- let condCode = this.unwrap(this.generate(condition, 'value'));
1210
- if (/[ <>=&|]/.test(condCode)) condCode = `(${condCode})`;
1211
- return `if (!${condCode}) ` + this.generate(body, 'statement');
1212
- }
1213
1192
  let [condition, thenBranch, ...elseBranches] = rest;
1214
1193
  return context === 'value'
1215
1194
  ? this.generateIfAsExpression(condition, thenBranch, elseBranches)
@@ -1433,11 +1412,6 @@ export class CodeGenerator {
1433
1412
  return code + (guard ? this.generateLoopBodyWithGuard(body, guard) : this.generateLoopBody(body));
1434
1413
  }
1435
1414
 
1436
- generateUntil(head, rest) {
1437
- let [cond, body] = rest;
1438
- return `while (!(${this.unwrap(this.generate(cond, 'value'))})) ` + this.generateLoopBody(body);
1439
- }
1440
-
1441
1415
  generateRange(head, rest) {
1442
1416
  if (head === '...') {
1443
1417
  if (rest.length === 1) return `...${this.generate(rest[0], 'value')}`;
@@ -1506,11 +1480,11 @@ export class CodeGenerator {
1506
1480
 
1507
1481
  generateRegexMatch(head, rest) {
1508
1482
  let [left, right] = rest;
1509
- this.helpers.add('toSearchable');
1483
+ this.helpers.add('toMatchable');
1510
1484
  this.programVars.add('_');
1511
1485
  let r = this.generate(right, 'value');
1512
1486
  let allowNL = r.includes('/m') ? ', true' : '';
1513
- return `(_ = toSearchable(${this.generate(left, 'value')}${allowNL}).match(${r}))`;
1487
+ return `(_ = toMatchable(${this.generate(left, 'value')}${allowNL}).match(${r}))`;
1514
1488
  }
1515
1489
 
1516
1490
  generateNew(head, rest) {
@@ -1648,20 +1622,18 @@ export class CodeGenerator {
1648
1622
  let [expr] = rest;
1649
1623
  if (Array.isArray(expr)) {
1650
1624
  let checkExpr = expr, wrapperType = null;
1651
- if (expr[0] === 'new' && Array.isArray(expr[1]) && (expr[1][0] === 'if' || expr[1][0] === 'unless')) {
1625
+ if (expr[0] === 'new' && Array.isArray(expr[1]) && expr[1][0] === 'if') {
1652
1626
  wrapperType = 'new'; checkExpr = expr[1];
1653
- } else if (expr[0] === 'if' || expr[0] === 'unless') {
1627
+ } else if (expr[0] === 'if') {
1654
1628
  checkExpr = expr;
1655
1629
  }
1656
- if (checkExpr[0] === 'if' || checkExpr[0] === 'unless') {
1657
- let [condType, condition, body] = checkExpr;
1630
+ if (checkExpr[0] === 'if') {
1631
+ let [, condition, body] = checkExpr;
1658
1632
  let unwrapped = Array.isArray(body) && body.length === 1 ? body[0] : body;
1659
1633
  expr = wrapperType === 'new' ? ['new', unwrapped] : unwrapped;
1660
1634
  let condCode = this.generate(condition, 'value');
1661
1635
  let throwCode = `throw ${this.generate(expr, 'value')}`;
1662
- return condType === 'unless'
1663
- ? `if (!(${condCode})) {\n${this.indent()} ${throwCode};\n${this.indent()}}`
1664
- : `if (${condCode}) {\n${this.indent()} ${throwCode};\n${this.indent()}}`;
1636
+ return `if (${condCode}) {\n${this.indent()} ${throwCode};\n${this.indent()}}`;
1665
1637
  }
1666
1638
  }
1667
1639
  let throwStmt = `throw ${this.generate(expr, 'value')}`;
@@ -1834,11 +1806,11 @@ export class CodeGenerator {
1834
1806
  if (typeof node === 'string' && (node === 'break' || node === 'continue')) return true;
1835
1807
  if (!Array.isArray(node)) return false;
1836
1808
  if (['break', 'continue', 'return', 'throw'].includes(node[0])) return true;
1837
- if (node[0] === 'if' || node[0] === 'unless') return node.slice(1).some(hasCtrl);
1809
+ if (node[0] === 'if') return node.slice(1).some(hasCtrl);
1838
1810
  return node.some(hasCtrl);
1839
1811
  };
1840
1812
 
1841
- let loopStmts = ['for-in', 'for-of', 'for-as', 'while', 'until', 'loop'];
1813
+ let loopStmts = ['for-in', 'for-of', 'for-as', 'while', 'loop'];
1842
1814
  if (this.is(expr, 'block')) {
1843
1815
  for (let i = 0; i < expr.length - 1; i++) {
1844
1816
  let s = expr[i + 1], isLast = i === expr.length - 2;
@@ -2142,7 +2114,7 @@ export class CodeGenerator {
2142
2114
  findPostfixConditional(expr) {
2143
2115
  if (!Array.isArray(expr)) return null;
2144
2116
  let h = expr[0];
2145
- if ((h === 'unless' || h === 'if') && expr.length === 3) return {type: h, condition: expr[1], value: expr[2]};
2117
+ if (h === 'if' && expr.length === 3) return {type: h, condition: expr[1], value: expr[2]};
2146
2118
  if (h === '+' || h === '-' || h === '*' || h === '/') {
2147
2119
  for (let i = 1; i < expr.length; i++) {
2148
2120
  let found = this.findPostfixConditional(expr[i]);
@@ -2236,7 +2208,7 @@ export class CodeGenerator {
2236
2208
  !this.scopeStack.some(s => s.has(v)) // don't re-declare variables from enclosing scopes
2237
2209
  ));
2238
2210
  let noRetStmts = ['return', 'throw', 'break', 'continue'];
2239
- let loopStmts = ['for-in', 'for-of', 'for-as', 'while', 'until', 'loop'];
2211
+ let loopStmts = ['for-in', 'for-of', 'for-as', 'while', 'loop'];
2240
2212
 
2241
2213
  // Track this function's scope so nested functions don't re-declare its variables
2242
2214
  this.scopeStack.push(new Set([...newVars, ...paramNames]));
@@ -2284,7 +2256,7 @@ export class CodeGenerator {
2284
2256
  return;
2285
2257
  }
2286
2258
 
2287
- if (!isConstructor && !sideEffectOnly && isLast && (h === 'if' || h === 'unless')) {
2259
+ if (!isConstructor && !sideEffectOnly && isLast && h === 'if') {
2288
2260
  let [cond, thenB, ...elseB] = stmt.slice(1);
2289
2261
  let hasMulti = (b) => this.is(b, 'block') && b.length > 2;
2290
2262
  if (hasMulti(thenB) || elseB.some(hasMulti)) {
@@ -2510,7 +2482,7 @@ export class CodeGenerator {
2510
2482
  generateIfElseWithEarlyReturns(ifStmt) {
2511
2483
  let [head, condition, thenBranch, ...elseBranches] = ifStmt;
2512
2484
  let code = '';
2513
- let condCode = head === 'unless' ? `!${this.generate(condition, 'value')}` : this.generate(condition, 'value');
2485
+ let condCode = this.generate(condition, 'value');
2514
2486
  code += this.indent() + `if (${condCode}) {\n`;
2515
2487
  code += this.withIndent(() => this.generateBranchWithReturn(thenBranch));
2516
2488
  code += this.indent() + '}';
@@ -2660,7 +2632,7 @@ export class CodeGenerator {
2660
2632
  if (!generated || generated.endsWith(';')) return false;
2661
2633
  if (!generated.endsWith('}')) return true;
2662
2634
  let h = Array.isArray(stmt) ? stmt[0] : null;
2663
- return !['def', 'class', 'if', 'unless', 'for-in', 'for-of', 'for-as', 'while', 'until', 'loop', 'switch', 'try'].includes(h);
2635
+ return !['def', 'class', 'if', 'for-in', 'for-of', 'for-as', 'while', 'loop', 'switch', 'try'].includes(h);
2664
2636
  }
2665
2637
 
2666
2638
  addSemicolon(stmt, generated) { return generated + (this.needsSemicolon(stmt, generated) ? ';' : ''); }
@@ -2687,7 +2659,7 @@ export class CodeGenerator {
2687
2659
  return stmts.some(s => Array.isArray(s) && ['return', 'throw', 'break', 'continue'].includes(s[0]));
2688
2660
  });
2689
2661
  }
2690
- if (t === 'if' || t === 'unless') {
2662
+ if (t === 'if') {
2691
2663
  let [, , thenB, elseB] = body;
2692
2664
  return this.branchHasControlFlow(thenB) && elseB && this.branchHasControlFlow(elseB);
2693
2665
  }
@@ -554,7 +554,7 @@ grammar =
554
554
  ]
555
555
 
556
556
  UnlessBlock: [
557
- o 'UNLESS Expression Block' , '["unless", 2, 3]'
557
+ o 'UNLESS Expression Block' , '["if", ["!", 2], 3]'
558
558
  o 'UNLESS Expression Block ELSE Block' , '["if", ["!", 2], 3, 5]'
559
559
  ]
560
560
 
@@ -565,8 +565,8 @@ grammar =
565
565
  o 'Statement POST_IF Expression' , '["if", 3, [1]]'
566
566
  o 'Expression POST_IF Expression' , '["if", 3, [1]]'
567
567
  o 'Expression POST_IF Expression ELSE INDENT Expression OUTDENT', '["?:", 3, 1, 6]'
568
- o 'Statement POST_UNLESS Expression' , '["unless", 3, [1]]'
569
- o 'Expression POST_UNLESS Expression', '["unless", 3, [1]]'
568
+ o 'Statement POST_UNLESS Expression' , '["if", ["!", 3], [1]]'
569
+ o 'Expression POST_UNLESS Expression', '["if", ["!", 3], [1]]'
570
570
  ]
571
571
 
572
572
  # Try/Catch/Finally
@@ -609,8 +609,8 @@ grammar =
609
609
  WhileSource: [
610
610
  o 'WHILE Expression' , '["while", 2]'
611
611
  o 'WHILE Expression WHEN Expression', '["while", 2, 4]'
612
- o 'UNTIL Expression' , '["until", 2]'
613
- o 'UNTIL Expression WHEN Expression', '["until", 2, 4]'
612
+ o 'UNTIL Expression' , '["while", ["!", 2]]'
613
+ o 'UNTIL Expression WHEN Expression', '["while", ["!", 2], 4]'
614
614
  ]
615
615
 
616
616
  While: [
package/src/parser-rd.js CHANGED
@@ -108,12 +108,12 @@ const ruleActions = (rule, vals, locs, shared) => {
108
108
  case 227: return ["yield-from", $[$0]];
109
109
  case 228: return ["if", $[$0-1], $[$0]];
110
110
  case 229: return $[$0-4].length === 3 ? ["if", $[$0-4][1], $[$0-4][2], ["if", $[$0-1], $[$0]]] : [...$[$0-4], ["if", $[$0-1], $[$0]]];
111
- case 230: return ["unless", $[$0-1], $[$0]];
111
+ case 230: return ["if", ["!", $[$0-1]], $[$0]];
112
112
  case 231: return ["if", ["!", $[$0-3]], $[$0-2], $[$0]];
113
113
  case 233: return $[$0-2].length === 3 ? ["if", $[$0-2][1], $[$0-2][2], $[$0]] : [...$[$0-2], $[$0]];
114
114
  case 235: case 236: return ["if", $[$0], [$[$0-2]]];
115
115
  case 237: return ["?:", $[$0-4], $[$0-6], $[$0-1]];
116
- case 238: case 239: return ["unless", $[$0], [$[$0-2]]];
116
+ case 238: case 239: return ["if", ["!", $[$0]], [$[$0-2]]];
117
117
  case 240: return ["try", $[$0]];
118
118
  case 241: return ["try", $[$0-1], $[$0]];
119
119
  case 242: return ["try", $[$0-2], $[$0]];
@@ -128,8 +128,8 @@ const ruleActions = (rule, vals, locs, shared) => {
128
128
  case 254: return ["when", $[$0-2], $[$0-1]];
129
129
  case 255: return ["while", $[$0]];
130
130
  case 256: return ["while", $[$0-2], $[$0]];
131
- case 257: return ["until", $[$0]];
132
- case 258: return ["until", $[$0-2], $[$0]];
131
+ case 257: return ["while", ["!", $[$0]]];
132
+ case 258: return ["while", ["!", $[$0-2]], $[$0]];
133
133
  case 259: return $[$0-1].length === 2 ? [$[$0-1][0], $[$0-1][1], $[$0]] : [$[$0-1][0], $[$0-1][1], $[$0-1][2], $[$0]];
134
134
  case 260: case 261: return $[$0].length === 2 ? [$[$0][0], $[$0][1], [$[$0-1]]] : [$[$0][0], $[$0][1], $[$0][2], [$[$0-1]]];
135
135
  case 263: return ["loop", $[$0]];
package/src/parser.js CHANGED
@@ -113,12 +113,12 @@ const parserInstance = {
113
113
  case 227: return ["yield-from", $[$0]];
114
114
  case 228: return ["if", $[$0-1], $[$0]];
115
115
  case 229: return $[$0-4].length === 3 ? ["if", $[$0-4][1], $[$0-4][2], ["if", $[$0-1], $[$0]]] : [...$[$0-4], ["if", $[$0-1], $[$0]]];
116
- case 230: return ["unless", $[$0-1], $[$0]];
116
+ case 230: return ["if", ["!", $[$0-1]], $[$0]];
117
117
  case 231: return ["if", ["!", $[$0-3]], $[$0-2], $[$0]];
118
118
  case 233: return $[$0-2].length === 3 ? ["if", $[$0-2][1], $[$0-2][2], $[$0]] : [...$[$0-2], $[$0]];
119
119
  case 235: case 236: return ["if", $[$0], [$[$0-2]]];
120
120
  case 237: return ["?:", $[$0-4], $[$0-6], $[$0-1]];
121
- case 238: case 239: return ["unless", $[$0], [$[$0-2]]];
121
+ case 238: case 239: return ["if", ["!", $[$0]], [$[$0-2]]];
122
122
  case 240: return ["try", $[$0]];
123
123
  case 241: return ["try", $[$0-1], $[$0]];
124
124
  case 242: return ["try", $[$0-2], $[$0]];
@@ -133,8 +133,8 @@ const parserInstance = {
133
133
  case 254: return ["when", $[$0-2], $[$0-1]];
134
134
  case 255: return ["while", $[$0]];
135
135
  case 256: return ["while", $[$0-2], $[$0]];
136
- case 257: return ["until", $[$0]];
137
- case 258: return ["until", $[$0-2], $[$0]];
136
+ case 257: return ["while", ["!", $[$0]]];
137
+ case 258: return ["while", ["!", $[$0-2]], $[$0]];
138
138
  case 259: return $[$0-1].length === 2 ? [$[$0-1][0], $[$0-1][1], $[$0]] : [$[$0-1][0], $[$0-1][1], $[$0-1][2], $[$0]];
139
139
  case 260: case 261: return $[$0].length === 2 ? [$[$0][0], $[$0][1], [$[$0-1]]] : [$[$0][0], $[$0][1], $[$0][2], [$[$0-1]]];
140
140
  case 263: return ["loop", $[$0]];