vaderjs 1.3.3-alpha-104 → 1.3.3-alpha-105

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/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "vaderjs",
3
3
  "description": "A Reactive library aimed to helping you build reactive applications inspired by react.js",
4
4
  "module": "vader.js",
5
- "version": "1.3.3-alpha-104",
5
+ "version": "1.3.3-alpha-105",
6
6
  "bin": {
7
7
  "vader": "./vader.js"
8
8
  },
package/runtime/router.js CHANGED
@@ -1 +1 @@
1
- import{Component}from"./vader.js";let middlewares=[];class VaderRouter{constructor(e,t){this.routes=[],this.middlewares=[],this.errorMiddlewares=[],this.listeners=[],this.basePath=e}get(e,t){this.routes.push({path:e,handler:t,method:"get"})}use(e){this.middlewares.push(e)}listen(e,t){e||(e=Math.random().toString(36).substring(7)),this.listeners.push(e),1===this.listeners.length?this.handleRoute(window.location.hash.length>0?window.location.hash:window.location.pathname):this.listeners.pop(),t&&t(),window.onhashchange=()=>{this.handleRoute(window.location.hash)}}extractParams(e,t){const s=e.split("/"),n=t.split("/"),o={};return s.forEach(((e,t)=>{if(e.startsWith(":")){const s=e.slice(1);o[s]=n[t]}else if(e.startsWith("*")){n.slice(t).forEach(((e,t)=>{o[t]=e}))}})),o}extractQueryParams(e){const t=e.split("?")[1];if(!t)return{};const s={};return t.split("&").forEach((e=>{const[t,n]=e.split("=");s[t]=n})),s}handleRoute(e){e=e.includes("#")?e.slice(1):e,console.log(e);let t=200,s=e,n=this.routes.find((t=>{if(t.path===e)return!0;if(""===e&&"/"===t.path)return!0;if(e.includes("?")&&(e=e.split("?")[0]),t.path.includes("*")||t.path.includes(":")){const s=t.path.split("/"),n=e.split("/");if(s.length!==n.length&&!t.path.endsWith("*"))return!1;for(let e=0;e<s.length;e++){const t=s[e],o=n[e];if(!t.startsWith(":")&&!t.startsWith("*")&&t!==o)return!1}return!0}const n=this.extractParams(t.path,s);return Object.keys(n).length>0}));n||(n=this.routes.find((e=>{if(e.path.includes("/404"))return this.error=!0,!0;!this.error&&e.path.includes("/404")&&(window.location.hash=this.basePath)})),t=n?200:404);const o=this.extractQueryParams(s),r=n&&n.path?this.extractParams(n.path,s):{};Object.keys(r).forEach((e=>{r[e]=r[e].split("?")?r[e].split("?")[0]:r[e]}));const i={headers:{},params:r,query:o,path:e,fileUrl:window.location.href.split(window.location.origin)[1],url:window.location.href,method:n?n.method:"get",pause:!1,timestamp:Date.now()};window.$CURRENT_URL=i.path,window.$FULL_URL=window.location.href.replace("#","");const a={status:t,log:e=>{void 0===e?console.log(`${i.path} ${i.method} ${a.status} ${i.timestamp}`):console.table({"Request Path":i.path,"Request Method":n.method,"Response Status":a.status,"Request Timestamp":i.timestamp})},refresh:()=>{this.handleRoute(window.location.hash)},redirect:e=>{!e.startsWith("/")&&(e=`/${e}`),window.location.hash=`#${e}`},render:async(e,t,s,n)=>{function i(e){return"function"==typeof e&&/^class\s/.test(Function.prototype.toString.call(e))}try{let n=new Component;if(i(e.default)){let t=new e.default;n.state=t.state,n=t}else{if(e.default.toString().includes("this.key"))throw new Error('Using this.key is not supported in functional components use the attribute key="a value" instead');n.key=e.default.toString().split('key="')[1]?e.default.toString().split('key="')[1].split('"')[0]:null;let i={key:n.key,render:()=>e.default.apply(n,[t,s]),request:t,response:s,params:r,queryParams:o,reset:n.reset.bind(n),onMount:n.onMount.bind(n),useState:null,router:{use:n.router.use.bind(n)},bindMount:n.bindMount.bind(n),memoize:n.memoize.bind(n),createComponent:n.createComponent.bind(n),isChild:!1,useState:n.useState.bind(n),parseStyle:n.parseStyle.bind(n),bind:n.bind.bind(n),useRef:n.useRef.bind(n),useReducer:n.useReducer.bind(n),onMount:n.onMount.bind(n),onUnmount:n.onUnmount.bind(n),hydrate:n.hydrate.bind(n)};n.render=i.render,n=i,console.log(t)}if(!document.querySelector("#root"))throw new Error("Root element not found, please add an element with id root");n.reset(),n.components={},n.request=t,n.response=s,n.router.use&&!n.isChild?await new Promise((async o=>{if(i(e.default))if(i(e.default))switch(await n.router.use(t,s),t.pause){case!0:console.log("pausing",t.pause);let e=setInterval((()=>{t.pause?console.log("still pausing",t.pause):(clearInterval(e),o())}),1e3);break;case!1:o()}else o();else switch(await e.default.apply(n,[t,s]),await n.router.use(t,s),t.pause){case!0:let e=setInterval((()=>{t.pause?console.log("still pausing request",t.url):(clearInterval(e),o())}),1e3);break;case!1:o()}})):n.router.use&&n.isChild&&console.warn("Router.use() is not supported in child components");const a=await n.render();document.querySelector("#root").innerHTML!==a&&(console.log("rendering"),document.querySelector("#root").innerHTML=a),n.bindMount(),n.onMount()}catch(e){console.error(e)}},setQuery:e=>{let t="";Object.keys(e).forEach(((s,n)=>{t+=`${0===n?"?":"&"}${s}=${e[s]}`}));let s=window.location.hash.split("?")[0];t=t.replace("/","-").replaceAll("/","-"),window.location.hash=`${s}${t}`},send:e=>{document.querySelector("#root").innerHTML=e},json:e=>{const t=document.querySelector("#root");t.innerHTML="";const s=document.createElement("pre");s.textContent=JSON.stringify(e,null,2),t.appendChild(s)}};middlewares.forEach((e=>{e(i,a)})),n&&n.handler(i,a)}}window.VaderRouter=VaderRouter;export default VaderRouter;
1
+ import{Component}from"./vader.js";let middlewares=[];class VaderRouter{constructor(e,t){this.routes=[],this.middlewares=[],this.errorMiddlewares=[],this.listeners=[],this.basePath=e,console.log(this.basePath)}get(e,t){this.routes.push({path:e,handler:t,method:"get"})}use(e){this.middlewares.push(e)}listen(e,t){e||(e=Math.random().toString(36).substring(7)),this.listeners.push(e),1===this.listeners.length?this.handleRoute(window.location.hash.length>0?window.location.hash:window.location.pathname):this.listeners.pop(),t&&t(),window.onpopstate=async e=>{let t=`/${window.location.pathname.split("/")[1]}`;routes.find((e=>e.url.includes(t)))||(t="/404");let n=(new DOMParser).parseFromString(await fetch(t,{cache:"reload"}).then((e=>e.text())),"text/html").documentElement;document.querySelector("#root").innerHTML=n.querySelector("#root").innerHTML,document.title=n.querySelector("title").innerHTML,document.querySelector('script[id="router"]').remove();let s=document.createElement("script");s.id="router",s.innerHTML=n.querySelector('script[id="router"]').innerHTML,s.setAttribute("type","module"),document.body.appendChild(s)}}extractParams(e,t){const n=e.split("/"),s=t.split("/"),o={};return n.forEach(((e,t)=>{if(e.startsWith(":")){const n=e.slice(1);o[n]=s[t]}else if(e.startsWith("*")){s.slice(t).forEach(((e,t)=>{o[t]=e}))}})),o}extractQueryParams(e){const t=e.split("?")[1];if(!t)return{};const n={};return t.split("&").forEach((e=>{const[t,s]=e.split("=");n[t]=s})),n}handleRoute(e){let t=200,n=e=e.includes("#")?e.slice(1):e,s=this.routes.find((t=>{if(t.path===e)return!0;if(""===e&&"/"===t.path)return!0;if(e.includes("?")&&(e=e.split("?")[0]),t.path.includes("*")||t.path.includes(":")){const n=t.path.split("/"),s=e.split("/");if(n.length!==s.length&&!t.path.endsWith("*"))return!1;for(let e=0;e<n.length;e++){const t=n[e],o=s[e];if(!t.startsWith(":")&&!t.startsWith("*")&&t!==o)return!1}return!0}const s=this.extractParams(t.path,n);return Object.keys(s).length>0}));s||(console.log(window.routes),s=window.routes.find((e=>{if(e.url.includes("/404"))window.history.replaceState({},"","/404");else if(!this.error&&e.url.includes("/404"))return!0})),t=s?200:404);const o=this.extractQueryParams(n),r=s&&s.path?this.extractParams(s.path,n):{};Object.keys(r).forEach((e=>{r[e]=r[e].split("?")?r[e].split("?")[0]:r[e]}));const i={headers:{},params:r,query:o,path:e,fileUrl:window.location.href.split(window.location.origin)[1],url:window.location.href,method:s?s.method:"get",pause:!1,timestamp:Date.now()};window.$CURRENT_URL=i.path,window.$FULL_URL=window.location.href.replace("#","");const a={status:t,log:e=>{void 0===e?console.log(`${i.path} ${i.method} ${a.status} ${i.timestamp}`):console.table({"Request Path":i.path,"Request Method":s.method,"Response Status":a.status,"Request Timestamp":i.timestamp})},refresh:()=>{this.handleRoute(window.location.hash)},redirect:e=>{!e.startsWith("/")&&(e=`/${e}`),window.location.hash=`#${e}`},render:async(e,t,n,s)=>{function isClass(e){return"function"==typeof e&&/^class\s/.test(Function.prototype.toString.call(e))}try{let s=new Component;if(isClass(e.default)){let t=new e.default;s.state=t.state,s=t}else{if(e.default.toString().includes("this.key"))throw new Error('Using this.key is not supported in functional components use the attribute key="a value" instead');s.key=e.default.toString().split('key="')[1]?e.default.toString().split('key="')[1].split('"')[0]:null;let i={key:s.key,render:()=>e.default.apply(s,[t,n]),request:t,response:n,params:r,queryParams:o,reset:s.reset.bind(s),onMount:s.onMount.bind(s),useState:null,router:{use:s.router.use.bind(s)},bindMount:s.bindMount.bind(s),memoize:s.memoize.bind(s),createComponent:s.createComponent.bind(s),isChild:!1,useState:s.useState.bind(s),parseStyle:s.parseStyle.bind(s),bind:s.bind.bind(s),useRef:s.useRef.bind(s),useReducer:s.useReducer.bind(s),onMount:s.onMount.bind(s),onUnmount:s.onUnmount.bind(s),hydrate:s.hydrate.bind(s)};s.render=i.render,s=i,console.log(t)}if(!document.querySelector("#root"))throw new Error("Root element not found, please add an element with id root");s.reset(),s.components={},s.request=t,s.response=n,s.router.use&&!s.isChild?await new Promise((async o=>{if(isClass(e.default))if(isClass(e.default))switch(await s.router.use(t,n),t.pause){case!0:console.log("pausing",t.pause);let e=setInterval((()=>{t.pause?console.log("still pausing",t.pause):(clearInterval(e),o())}),1e3);break;case!1:o()}else o();else switch(await e.default.apply(s,[t,n]),await s.router.use(t,n),t.pause){case!0:let e=setInterval((()=>{t.pause?console.log("still pausing request",t.url):(clearInterval(e),o())}),1e3);break;case!1:o()}})):s.router.use&&s.isChild&&console.warn("Router.use() is not supported in child components");const i=await s.render();document.querySelector("#root").innerHTML!==i&&(console.log("rendering"),document.querySelector("#root").innerHTML=i),s.bindMount(),s.onMount()}catch(e){console.error(e)}},setQuery:e=>{let t="";Object.keys(e).forEach(((n,s)=>{t+=`${0===s?"?":"&"}${n}=${e[n]}`}));let n=window.location.hash.split("?")[0];t=t.replace("/","-").replaceAll("/","-"),window.location.hash=`${n}${t}`},send:e=>{document.querySelector("#root").innerHTML=e},json:e=>{const t=document.querySelector("#root");t.innerHTML="";const n=document.createElement("pre");n.textContent=JSON.stringify(e,null,2),t.appendChild(n)}};middlewares.forEach((e=>{e(i,a)})),s&&s.handler(i,a)}}window.VaderRouter=VaderRouter;export default VaderRouter;
package/runtime/vader.js CHANGED
@@ -1 +1 @@
1
- window.Vader={version:"1.3.3"},window.componentRegistry={};let errors={"SyntaxError: Unexpected token '<'":"You forgot to enclose tags in a fragment <></>"},mounts=[],hasRan=[];export const strictMount=(e,t)=>{let n=setInterval((()=>{document.querySelector(`[key="${e}"]`)&&!hasRan.includes(e)&&(clearInterval(n),t(),hasRan.push(e))}),120)};export class Component{constructor(){this.state={},this.key=null,this.components={},this.mounted=!1,this.checkIFMounted(),this.memoizes=[],this.functions=[],this.children=[],this.parentNode={},this.request={headers:{},method:"GET",params:{},path:"",query:{}},this.response={json:e=>{},send:e=>{},redirect:e=>{},render:async e=>{},log:e=>{},setQuery:e=>{}},this.router={use:e=>{}}}createComponent(e,t,n){function s(e){return"function"==typeof e&&/^class\s/.test(Function.prototype.toString.call(e))}let r=s(e)?new e(t):null;if(!e)throw new Error("Component must be defined");let o=new Component(t);if(s(e))r.props=t,r.children=n,r.props.children=n.join(""),r.parentNode=this,r.request=this.request,r.response=this.response,r.key=t.key||null,o=r;else{e.toString();o.key=e.toString().split('key="')[1]?e.toString().split('key="')[1].split('"')[0]:null;let n={key:o.key,render:()=>e.apply(o,[t]),request:this.request,isChild:!0,response:this.response,params:this.request.params,queryParams:this.request.query,reset:o.reset.bind(o),onMount:o.onMount.bind(o),useState:null,router:{use:o.router.use.bind(o)},bindMount:o.bindMount.bind(o),memoize:o.memoize.bind(o),createComponent:o.createComponent.bind(o),isChild:!1,useState:o.useState.bind(o),parseStyle:o.parseStyle.bind(o),bind:o.bind.bind(o),useRef:o.useRef.bind(o),request:this.request,response:this.response,useReducer:o.useReducer.bind(o),hydrate:o.hydrate.bind(o),onUnmount:o.onUnmount.bind(o),parentNoe:this};o.render=n.render,o=n}return this.components[t.key]||(this.components[t.key]=o),!this.children.includes(o)&&this.children.push(o),this.components[t.key]}reset(){Object.keys(this.components).forEach((e=>{this.components[e].onUnmount(),delete this.components[e]})),this.state={},this.children=[]}memoize(e){if(!e.key)throw new Error("Component must have a static key");if(!0==!this.memoizes.includes(e.key))this.memoizes.push(e.key),this.components[e.key]=e;let t=this.components[e.key];t.bindMount(),t.parentNode=this,t.props=e.props,t.request=this.request,t.response=this.response,t.onMount=e.onMount.bind(e),t.onUnmount=e.onUnmount.bind(e);let n=t.render();return n&&n.split(">,").length>1&&(n=n.replaceAll(">,",">")),`<div key="${e.key}">${n}</div>`}parseStyle(e){let t="";return Object.keys(e).forEach((n=>{let s=e[n];n=n.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g,"$1-$2").toLowerCase(),t+=`${n}:${s};`})),t}bindMount(){mounts.push(this)}domDifference(e,t){let n=[];for(let s=0;s<e.length;s++){let r=e[s],o=t[s];r&&o&&!r.isEqualNode(o)&&n.push({type:"replace",old:r,new:o.cloneNode(!0)})}return n}updateChangedElements(e){e.forEach((e=>{switch(e.type){case"replace":e.old.parentNode.replaceChild(e.new,e.old);break;case"remove":e.old.remove();break;case"add":e.old.appendChild(e.new.cloneNode(!0))}}))}hydrate(e){if(e){console.log(e);let t=(new DOMParser).parseFromString(this.render(),"text/html").body.querySelector(`[ref="${e}"]`),n=document.querySelector(`[ref="${e}"]`);console.log(t,n)}else{let e=this.key?document.querySelector(`[key="${this.key}"]`):null,t=(new DOMParser).parseFromString(this.render(),"text/html").body;if(t=document.createElement("div").appendChild(t),!e&&(e=document.querySelector(`[key="${t.attributes?.key?.value||null}"]`)),!e)return void console.error('Hydration failed, component not found got ensure you have set key="a value" on the component or this.key inside of function or render method body');t.querySelectorAll("*").forEach((e=>{e.hasAttribute("key")&&e.innerHTML!==document.querySelector(`[key="${e.attributes.key.value}"]`).innerHTML&&document.querySelector(`[key="${e.attributes.key.value}"]`).replaceWith(e)}))}}patch(e,t){const n=this.domDifference(e,t);this.updateChangedElements(n)}handleObject(obj){try{obj=JSON.parse(obj)}catch(e){}return eval(obj)}bind(e,t,n,s,...r){n=n+this.key||2022;let o={},i=(s=s.replace(/,,/g,",")).replaceAll(",,",",");for(var u in r){let e=r[u];o[i.split(",")[u]]=e}s=s.replace(",,",",");let a=null;try{a=new Function(`event, ${s}`,` \n return (async (event, ${s}) => { \n ${e.toString()}\n })(event, ${Object.keys(o).join(",")}) \n `)}catch(e){let{message:t}=e;console.error(`Error in function ${n} ${t}`)}return a=a.bind(this),this.functions.find((e=>e.ref===n))||document.addEventListener(`$dispatch_#id=${n}`,(e=>{let{name:t,event:s}=e.detail;if(t===n){let e=this.functions.find((e=>e.ref===n)).params;Object.keys(e).forEach((t=>{e[t]instanceof CustomEvent&&delete e[t],void 0===e[t]?delete e[t]:e[t]})),a(s,...Object.values(e))}})),window.callFunction=(e,t)=>{document.dispatchEvent(new CustomEvent(`$dispatch_#id=${e}`,{detail:{name:e,params:null,event:t}}))},!this.functions.find((e=>e.ref===n))&&this.functions.push({ref:n,params:o}),t?e:`((event)=>{event.target.ev = event; callFunction('${n}', event.target.ev)})(event)`}useState(e,t){this.state[e]||(this.state[e]=t);let n=()=>this.state[e],s=n();return[s,(t,r)=>{this.state[e]=t,this.hydrate(r),s=n()}]}useRef(e=null,t){this.state[e]||(this.state[e]=t);return{bind:e+this.key,current:(()=>document.querySelector(`[ref="${e+this.key}"]`)||t)()}}useReducer(e=null,t,n=null){this.state[e]||(this.state[e]=t);const s=()=>this.state[e];let r=s();return[s(),(t,o)=>{const i=n(r,t)??t;this.state[e]=i,this.hydrate(o),r=s()}]}render(){}checkIFMounted(){new MutationObserver((e=>{e.forEach((e=>{e.target.querySelector(`[key="${this.key}"]`)&&!this.mounted&&(this.onMount(),this.mounted=!0),Array.from(e.removedNodes).find((e=>e.attributes&&e.attributes.key&&e.attributes.key.value===this.key))&&(this.onUnmount(),this.reset())}))})).observe(document.body,{childList:!0,subtree:!0})}onMount(){}onUnmount(){}}export const useState=(e,t)=>{states[e]||(states[e]=t);return[states[e],(t,n)=>{states[e]=t,this.hydrate(n)}]};export const useReducer=(e,t)=>[e,e=>{}];export const useRef=e=>({current:e,bind:""});export default{Component:Component,useRef:useRef,useReducer:useReducer,useState:useState,strictMount:strictMount};
1
+ window.Vader={version:"1.3.3"},window.componentRegistry={};let errors={"SyntaxError: Unexpected token '<'":"You forgot to enclose tags in a fragment <></>"},mounts=[],hasRan=[];export const strictMount=(e,t)=>{let n=setInterval((()=>{document.querySelector(`[key="${e}"]`)&&!hasRan.includes(e)&&(clearInterval(n),t(),hasRan.push(e))}),120)};export class Component{constructor(){this.state={},this.key=null,this.components={},this.mounted=!1,this.checkIFMounted(),this.memoizes=[],this.functions=[],this.children=[],this.parentNode={},this.request={headers:{},method:"GET",params:{},path:"",query:{}},this.response={json:e=>{},send:e=>{},redirect:e=>{},render:async e=>{},log:e=>{},setQuery:e=>{}},this.router={use:e=>{}}}createComponent(e,t,n){function isClass(e){return"function"==typeof e&&/^class\s/.test(Function.prototype.toString.call(e))}let s=isClass(e)?new e(t):null;if(!e)throw new Error("Component must be defined");let r=new Component(t);if(isClass(e))s.props=t,s.children=n,s.props.children=n.join(""),s.parentNode=this,s.request=this.request,s.response=this.response,s.key=t.key||null,r=s;else{e.toString();r.key=e.toString().split('key="')[1]?e.toString().split('key="')[1].split('"')[0]:null;let n={key:r.key,render:()=>e.apply(r,[t]),request:this.request,isChild:!0,response:this.response,params:this.request.params,queryParams:this.request.query,reset:r.reset.bind(r),onMount:r.onMount.bind(r),useState:null,router:{use:r.router.use.bind(r)},bindMount:r.bindMount.bind(r),memoize:r.memoize.bind(r),createComponent:r.createComponent.bind(r),isChild:!1,useState:r.useState.bind(r),parseStyle:r.parseStyle.bind(r),bind:r.bind.bind(r),useRef:r.useRef.bind(r),request:this.request,response:this.response,useReducer:r.useReducer.bind(r),hydrate:r.hydrate.bind(r),onUnmount:r.onUnmount.bind(r),parentNoe:this};r.render=n.render,r=n}return this.components[t.key]||(this.components[t.key]=r),!this.children.includes(r)&&this.children.push(r),this.components[t.key]}reset(){Object.keys(this.components).forEach((e=>{this.components[e].onUnmount(),delete this.components[e]})),this.state={},this.children=[]}memoize(e){if(!0==!this.memoizes.includes(e.key))this.memoizes.push(e.key),this.components[e.key]=e;let t=this.components[e.key];t.bindMount(),t.parentNode=this,t.props=e.props,t.request=this.request,t.response=this.response,t.onMount=e.onMount.bind(e),t.onUnmount=e.onUnmount.bind(e);let n=t.render();return n&&n.split(">,").length>1&&(n=n.replaceAll(">,",">")),`<span key="${e.key}" >${n}</span>`}parseStyle(e){let t="";return Object.keys(e).forEach((n=>{let s=e[n];n=n.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g,"$1-$2").toLowerCase(),t+=`${n}:${s};`})),t}bindMount(){mounts.push(this)}domDifference(e,t){let n=[];for(let s=0;s<e.length;s++){let r=e[s],o=t[s];r&&o&&!r.isEqualNode(o)&&n.push({type:"replace",old:r,new:o.cloneNode(!0)})}return n}updateChangedElements(e){e.forEach((e=>{switch(e.type){case"replace":e.old.parentNode.replaceChild(e.new,e.old);break;case"remove":e.old.remove();break;case"add":e.old.appendChild(e.new.cloneNode(!0))}}))}hydrate(e){if(e){(new DOMParser).parseFromString(this.render(),"text/html").body.querySelector(`[ref="${e}"]`),document.querySelector(`[ref="${e}"]`)}else{let e=this.key?document.querySelector(`[key="${this.key}"]`):null,t=(new DOMParser).parseFromString(this.render(),"text/html").body;if(t=document.createElement("div").appendChild(t),!e&&(e=document.querySelector(`[key="${t.attributes?.key?.value||null}"]`)),!e)return void console.error('Hydration failed, component not found got ensure you have set key="a value" on the component or this.key inside of function or render method body');t.querySelectorAll("*").forEach((e=>{e.hasAttribute("key")&&e.innerHTML!==document.querySelector(`[key="${e.attributes.key.value}"]`).innerHTML&&document.querySelector(`[key="${e.attributes.key.value}"]`).replaceWith(e)}))}}patch(e,t){const n=this.domDifference(e,t);this.updateChangedElements(n)}handleObject(obj){try{obj=JSON.parse(obj)}catch(e){}return eval(obj)}bind(e,t,n,s,...r){n=n+this.key||2022;let o={},i=(s=s.replace(/,,/g,",")).replaceAll(",,",",");for(var u in r){let e=r[u];o[i.split(",")[u]]=e}s=s.replace(",,",",");let a=null;e=e.split("\n").join(";");try{a=new Function(`event, ${s}`,` \n return (async (event, ${s}) => { \n ${e.toString()}\n })(event, ${Object.keys(o).join(",")}) \n `)}catch(e){let{message:t}=e;console.error(`Error in function ${n} ${t}`)}return a=a.bind(this),this.functions.find((e=>e.ref===n))||document.addEventListener(`$dispatch_#id=${n}`,(e=>{let{name:t,event:s}=e.detail;if(t===n){let e=this.functions.find((e=>e.ref===n)).params;Object.keys(e).forEach((t=>{e[t]instanceof CustomEvent&&delete e[t],void 0===e[t]?delete e[t]:e[t]})),a(s,...Object.values(e))}})),window.callFunction=(e,t)=>{document.dispatchEvent(new CustomEvent(`$dispatch_#id=${e}`,{detail:{name:e,params:null,event:t}}))},!this.functions.find((e=>e.ref===n))&&this.functions.push({ref:n,params:o}),t?e:`((event)=>{event.target.ev = event; callFunction('${n}', event.target.ev)})(event)`}useState(e,t){this.state[e]||(this.state[e]=t);let updatedValue=()=>this.state[e],n=updatedValue();return[n,(t,s)=>{this.state[e]=t,this.hydrate(s),n=updatedValue()}]}useRef(e=null,t){this.state[e]||(this.state[e]=t);return{bind:e+this.key,current:(()=>document.querySelector(`[ref="${e+this.key}"]`)||t)()}}useReducer(e=null,t,n=null){this.state[e]||(this.state[e]=t);const getValue=()=>this.state[e];let s=getValue();return[getValue(),(t,r)=>{const o=n(s,t)??t;this.state[e]=o,this.hydrate(r),s=getValue()}]}render(){}checkIFMounted(){new MutationObserver((e=>{e.forEach((e=>{e.target.querySelector(`[key="${this.key}"]`)&&!this.mounted&&(this.onMount(),this.mounted=!0),Array.from(e.removedNodes).find((e=>e.attributes&&e.attributes.key&&e.attributes.key.value===this.key))&&(this.onUnmount(),this.reset())}))})).observe(document.body,{childList:!0,subtree:!0})}onMount(){}onUnmount(){}}export const useState=(e,t)=>{states[e]||(states[e]=t);return[states[e],(t,n)=>{states[e]=t,this.hydrate(n)}]};export const useReducer=(e,t)=>[e,e=>{}];export const useRef=e=>({current:e,bind:""});export class Link extends Component{constructor(e){super(e),this.props=e,this.link=document.createElement("a")}render(){return this.link.innerHTML=this.props.children,this.link.setAttribute("id",this.props?.href),this.link.style=this.props?.style,this.link.setAttribute("class",this.props?.class),this.link.setAttribute("onclick",`window.history.pushState({}, '', '${this.props?.href}'); window.dispatchEvent(new Event('popstate'));`),this.link.outerHTML}}export default{Component:Component,useRef:useRef,useReducer:useReducer,useState:useState,strictMount:strictMount};
package/vader.js CHANGED
@@ -3,6 +3,9 @@ import fs from "fs";
3
3
  import { glob, globSync, globStream, globStreamSync, Glob, } from 'glob'
4
4
  import puppeteer from 'puppeteer';
5
5
  import http from 'http'
6
+ import { WebSocketServer } from 'ws'
7
+ import { watch } from "fs";
8
+ let start = Date.now()
6
9
  let bundleSize = 0;
7
10
  let errorCodes = {
8
11
  "SyntaxError: Unexpected token '<'": "You forgot to enclose tags in a fragment <></>",
@@ -17,7 +20,8 @@ if(!fs.existsSync(process.cwd() + '/dist')){
17
20
  }
18
21
 
19
22
 
20
- if (process.env.isCloudflare || !process.cwd() + '/dist/index.html') {
23
+
24
+ if (typeof process.env.isCloudflare !== "undefined" || !fs.existsSync(process.cwd() + '/dist/index.html')) {
21
25
  let htmlFile = fs.readFileSync(process.cwd() + "/node_modules/vaderjs/runtime/index.html", 'utf8')
22
26
  fs.writeFileSync(process.cwd() + "/dist/index.html", htmlFile)
23
27
  }
@@ -88,8 +92,7 @@ function Compiler(func, file) {
88
92
  /\s*([a-zA-Z0-9_-]+)(\s*=\s*("([^"\\]*(\\.[^"\\]*)*)"|'([^'\\]*(\\.[^'\\]*)*)'|\{(?:[^{}]|(?:\{(?:[^{}]|(?:\{[^{}]*\})*)*\})*)*\}|(?:\([^)]*\)|\{[^}]*\}|()=>\s*(?:\{[^}]*\})?)|\[[^\]]*\]))?/gs;
89
93
 
90
94
  // only return elements with attribute {()=>{}} or if it also has parameters ex: onclick={(event)=>{console.log(event)}} also get muti line functions
91
- const functionAttributeRegex =
92
- /\s*([a-zA-Z0-9_-]+)(\s*=\s*{(?:[^{}]|(?:\{(?:[^{}]|(?:\{[^{}]*\})*)*\})*)*})/gs;
95
+ const functionAttributeRegex = /\s*([a-zA-Z0-9_-]+)(\s*=\s*{(.*?)}|\s*=\s*function\s*(.*?))(?=\s*[a-zA-Z0-9_-]+\s*=|\s*>)/gs;
93
96
 
94
97
  let attributesList = [];
95
98
 
@@ -97,7 +100,7 @@ function Compiler(func, file) {
97
100
  let functionAttributes = [];
98
101
  let functionMatch;
99
102
  while ((functionMatch = functionAttributeRegex.exec(code)) !== null) {
100
- let [, attributeName, attributeValue] = functionMatch;
103
+ let [, attributeName, attributeValue] = functionMatch;
101
104
  let attribute = {};
102
105
 
103
106
  if (attributeValue && attributeValue.includes("=>") || attributeValue && attributeValue.includes("function")) {
@@ -134,14 +137,19 @@ function Compiler(func, file) {
134
137
  isJSXComponent = elementTag.match(/^[A-Z]/) ? true : false;
135
138
  }
136
139
  });
140
+ // add ; after newlines
141
+
142
+
137
143
  let newvalue = attributeValue.includes('=>') ? attributeValue.split("=>").slice(1).join("=>").trim() : attributeValue.split("function").slice(1).join("function").trim()
138
144
 
145
+
139
146
 
140
147
  newvalue = newvalue.trim();
141
148
 
142
149
  //remove starting {
143
- newvalue = newvalue.replace("{", "");
144
-
150
+ newvalue = newvalue.replace("{", "")
151
+
152
+
145
153
 
146
154
  let params = attributeValue
147
155
  .split("=>")[0]
@@ -156,39 +164,38 @@ function Compiler(func, file) {
156
164
  // split first {}
157
165
  newvalue = newvalue.trim();
158
166
 
159
- if (newvalue.startsWith("{")) {
160
- newvalue = newvalue.split("{")[1];
161
- }
167
+
162
168
 
163
- switch (true) {
164
- case newvalue.endsWith("}}"):
165
- newvalue = newvalue.replace("}}", "");
166
- break;
167
- case newvalue.endsWith("}"):
168
- newvalue = newvalue.replace("}", "");
169
- break;
170
- }
171
- // replace trailing }
169
+ newvalue = newvalue.replace(/}\s*$/, '');
170
+
171
+
172
+
172
173
  newvalue = newvalue.trim();
173
- if (newvalue.endsWith("}")) {
174
- newvalue = newvalue.replace("}", "");
175
- }
176
- functionparams.length > 0 ? params = params + ',' + functionparams.map((e) => e.name).join(',') : null
177
- newvalue = newvalue.split('\n').map(line => line.trim() ? line.trim() + ';' : line).join('\n');
174
+
175
+ // remmove trailing }
176
+
177
+ newvalue = newvalue.trim();
178
+ newvalue = newvalue.replace(/}\s*$/, '');
179
+
180
+
181
+
182
+ functionparams.length > 0 ? params = params + ',' + functionparams.map((e) => e.name).join(',') : null
183
+
178
184
  newvalue = newvalue.replaceAll(',,', ',')
179
185
  let paramnames = params ? params.split(',').map((e) => e.trim()) : null
180
186
  paramnames = paramnames ? paramnames.filter((e) => e.length > 0) : null
181
187
  // remove comments
182
188
  paramnames = paramnames ? paramnames.map((e) => e.match(/\/\*.*\*\//gs) ? e.replace(e.match(/\/\*.*\*\//gs)[0], "") : e) : null
189
+ newvalue = newvalue.replaceAll(/\s+/g, " ")
183
190
 
184
- let bind = isJSXComponent ? `${attributeName}=function(${params}){${newvalue}}.bind(this)` : `${attributeName} = "\$\{this.bind("${newvalue.replace(/\s+g/, " ")}", ${isJSXComponent}, "${ref}", "${paramnames ? paramnames.map((e, index) => {
191
+ let bind = isJSXComponent ? `${attributeName}=function(${params}){${newvalue}}.bind(this)` : `${attributeName}="\$\{this.bind("${newvalue.replace(/\s+g/, " ")}", ${isJSXComponent}, "${ref}", "${paramnames ? paramnames.map((e, index) => {
185
192
  if (e.length < 1) return ''
186
193
  if (e.length > 0) {
187
194
  index == 0 ? e : ',' + e
188
195
  }
189
196
  return e
190
197
  }) : ''}" ${params ? params.split(',').map((e) => e.trim()).filter(Boolean).map((e) => `,${e}`).join('') : ''})}"`
191
- bind = bind.replaceAll(/\s+/g, " ");
198
+
192
199
  string = string.replace(old, bind);
193
200
  }
194
201
  }
@@ -710,6 +717,7 @@ async function Build() {
710
717
  let appjs = '';
711
718
  let hasWritten = []
712
719
  function ssg(routes = []) {
720
+ globalThis.isBuilding = true
713
721
  console.log(`Generating html files for ${routes.length} routes`)
714
722
  routes.forEach(async (route) => {
715
723
  if(route.url.includes(':')){
@@ -724,7 +732,7 @@ async function Build() {
724
732
  if(url&&route.url === url){
725
733
  return e
726
734
  }else{
727
- return null
735
+ return null
728
736
 
729
737
  }
730
738
  }
@@ -734,6 +742,9 @@ async function Build() {
734
742
  <!DOCTYPE html>
735
743
  <html lang="en">
736
744
  <head>
745
+ <script>
746
+ window.routes = JSON.parse('${JSON.stringify(routes)}')
747
+ </script>
737
748
  <meta charset="UTF-8">
738
749
  <meta name="viewport" content="width=device-width,initial-scale=1.0">
739
750
  <script type="module" id="meta">
@@ -791,7 +802,7 @@ async function Build() {
791
802
  <div id="root"></div>
792
803
  </body>
793
804
 
794
- <script type="module">
805
+ <script type="module" id="router">
795
806
  import VaderRouter from '/router.js'
796
807
  const router = new VaderRouter('${route.url}', 3000)
797
808
  router.get('${route.url}', async (req, res) => {
@@ -799,9 +810,7 @@ async function Build() {
799
810
  res.render(module, req, res, module.$metadata)
800
811
  })
801
812
  ${equalparamroute.length > 0 ? equalparamroute.map((e) => {
802
- let folderName = '/' + e.url.split('/')[1]
803
-
804
- e.url = e.url.replace(folderName, '')
813
+
805
814
 
806
815
  return `router.get('${e.url}', async (req, res) => {
807
816
  let module = await import('/${e.fileName.replace('.jsx', '.js')}')
@@ -809,13 +818,14 @@ async function Build() {
809
818
  })\n`
810
819
  }): ''}
811
820
  router.listen(3000)
821
+
812
822
  </script>
813
823
  </html>
814
824
  `;
815
825
 
816
826
  let port = Math.floor(Math.random() * 10000) + 1;
817
827
 
818
- const server = http.createServer((req, res) => {
828
+ const server = http.createServer((req, res) => {
819
829
  if (req.url === '/') {
820
830
  // Respond with the generated HTML
821
831
  res.writeHead(200, { 'Content-Type': 'text/html' });
@@ -830,7 +840,7 @@ async function Build() {
830
840
  res.end('File not found');
831
841
  } else {
832
842
  res.writeHead(200, { 'Content-Type': filePath.includes('js') ? 'text/javascript' : 'text/html' });
833
- res.end(data);
843
+ res.end(data);
834
844
  }
835
845
  });
836
846
  }
@@ -845,7 +855,7 @@ async function Build() {
845
855
  }).then(async (browser) => {
846
856
 
847
857
  // remove /: from route
848
- route.url = route.url.replaceAll(/\/:[a-zA-Z0-9_-]+/gs, '')
858
+ route.url = route.url.replaceAll(/\/:[a-zA-Z0-9_-]+/gs, '')
849
859
  const page = await browser.newPage();
850
860
  await page.goto(`http://localhost:${port}` + '#' + route.url, { waitUntil: 'networkidle2' });
851
861
  await page.waitForSelector('#root');
@@ -858,13 +868,15 @@ async function Build() {
858
868
  await writer(process.cwd() + '/dist/' + (isBasePath ? 'index.html' : `${route.url}/` + 'index.html'), html)
859
869
  await browser.close();
860
870
  // close http
861
- server.close()
862
- console.log(`Generated html file for route ${route.url}`)
871
+ server.close()
863
872
  })
864
873
 
865
874
  })
866
-
867
- console.log('Done')
875
+
876
+ let timeout = setTimeout(() => {
877
+ globalThis.isBuilding = false
878
+ clearTimeout(timeout)
879
+ }, 1000)
868
880
  }
869
881
 
870
882
  globalThis.routes = []
@@ -927,11 +939,10 @@ async function Build() {
927
939
 
928
940
 
929
941
 
930
- globalThis.routes.push({fileName:fileName, url:obj.url})
942
+ globalThis.routes.push({fileName:fileName, url:obj.url, html:'/' + (isBasePath ? 'index.html' : `${obj.url}/` + 'index.html')})
931
943
 
932
944
 
933
-
934
-
945
+
935
946
  }
936
947
 
937
948
  ssg(globalThis.routes)
@@ -1030,11 +1041,87 @@ async function Build() {
1030
1041
  }
1031
1042
 
1032
1043
  globalThis.isBuilding = false
1033
- console.log(`Build complete! ${Math.round(bundleSize / 1000)}${bundleSize > 1000 ? 'kb' : 'bytes'} written to ./dist`)
1044
+ console.log(`Build completed in ${Date.now() - start}ms with ${Math.round(bundleSize / 1000)}kb`)
1045
+
1034
1046
  bundleSize = 0;
1035
1047
  return true
1048
+ }
1049
+ const s = ()=>{
1050
+
1051
+ const server = http.createServer((req, res) => {
1052
+
1053
+ if(!req.url.endsWith('.js') && !req.url.endsWith('.css') && !req.url.endsWith('.mjs') && !req.url.endsWith('.cjs') && !req.url.endsWith('.html') && !req.url.endsWith('.json')){
1054
+ req.url = req.url !== '/' ? req.url.split('/')[1] : req.url
1055
+ req.url = process.cwd() + '/dist/' + req.url + '/index.html'
1056
+ }else{
1057
+ req.url = process.cwd() + '/dist/' + req.url
1058
+ }
1059
+
1060
+ const filePath = req.url
1061
+
1062
+ fs.readFile(filePath, (err, data) => {
1063
+ if (err) {
1064
+ res.writeHead(404, { 'Content-Type': 'text/html' });
1065
+ res.end(fs.existsSync(process.cwd() + '/dist/404') ? fs.readFileSync(process.cwd() + '/dist/404/index.html') : '404');
1066
+ } else {
1067
+ const contentType = getContentType(filePath);
1068
+ switch(true){
1069
+ case contentType === 'text/html' && globalThis.devMode:
1070
+ data = data.toString() + `<script type="module">
1071
+ let ws = new WebSocket('ws://localhost:3000')
1072
+ ws.onmessage = (e) => {
1073
+ if(e.data === 'reload'){
1074
+ console.log('Reloading page...')
1075
+ window.route.hydrate()
1076
+ }
1077
+ }
1078
+ </script>
1079
+ `
1080
+ }
1081
+ res.writeHead(200, { 'Content-Type': contentType });
1082
+ res.end(data);
1083
+ }
1084
+ });
1085
+ });
1086
+
1087
+
1088
+ const ws = new WebSocketServer({ server });
1089
+ ws.on('connection', (socket) => {
1090
+ console.log('Client connected');
1091
+ socket.on('close', () => console.log('Client disconnected'));
1092
+ });
1093
+
1094
+
1095
+ function getContentType(filePath) {
1096
+ if (filePath.includes('js')) {
1097
+ return 'text/javascript';
1098
+ } else if (filePath.includes('.css')) {
1099
+ return 'text/css';
1100
+ } else {
1101
+ return 'text/html';
1102
+ }
1036
1103
  }
1037
- import { watch } from "fs";
1104
+
1105
+ const PORT = process.env.PORT || 3000;
1106
+ server.listen(PORT, () => {
1107
+ console.log(`Server is running on port ${PORT}`);
1108
+ });
1109
+ let i =
1110
+ setInterval(() => {
1111
+ if (globalThis.isBuilding && globalThis.devMode) {
1112
+ // reload page
1113
+ console.log('Reloading page...')
1114
+ ws.clients.forEach((client) => {
1115
+ client.send('reload')
1116
+ console.log('Reloaded page')
1117
+ })
1118
+ } else{
1119
+ clearInterval(i)
1120
+ }
1121
+ },120)
1122
+
1123
+ }
1124
+
1038
1125
 
1039
1126
  switch (true) {
1040
1127
  case process.argv.includes('--watch'):
@@ -1061,7 +1148,10 @@ Vader.js v1.3.3
1061
1148
  }
1062
1149
  }).on('error', (err) => console.log(err))
1063
1150
  })
1064
-
1151
+
1152
+ s()
1153
+
1154
+ globalThis.listen = true;
1065
1155
 
1066
1156
  break;
1067
1157
  case process.argv.includes('--build'):
@@ -1069,8 +1159,20 @@ Vader.js v1.3.3
1069
1159
  console.log(`
1070
1160
  Vader.js v1.3.3
1071
1161
  Building to ./dist
1072
- `)
1162
+ `)
1073
1163
  Build()
1164
+
1165
+ break;
1166
+ case process.argv.includes('--serve'):
1167
+ let port = process.argv[process.argv.indexOf('--serve') + 1] || 3000
1168
+ process.env.PORT = port
1169
+ globalThis.devMode = false
1170
+ console.log(`
1171
+ Vader.js v1.3.3
1172
+ Serving ./dist on port ${port}
1173
+ url: http://localhost:${port}
1174
+ `)
1175
+ s()
1074
1176
  break;
1075
1177
  default:
1076
1178
  console.log(`
@@ -1079,9 +1181,11 @@ Vader.js is a reactive framework for building interactive applications for the w
1079
1181
  Usage: vader <command>
1080
1182
 
1081
1183
  Commands:
1082
- --watch Watch the pages folder for changes and recompile
1184
+ --watch Watch the pages folder for changes with hot reloading
1083
1185
 
1084
1186
  --build Build the project
1187
+
1188
+ --serve Serve the project on a given port
1085
1189
  Learn more about vader: https://vader-js.pages.dev/
1086
1190
 
1087
1191
  `)