vaderjs 1.3.3-5b5a772ebe58 → 1.3.3-7924566dd811

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -14,7 +14,7 @@
14
14
  [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/Postr-Inc/Vader.js/blob/main/LICENSE) [![npm version](https://img.shields.io/npm/v/vaderjs.svg?style=flat)](https://www.npmjs.com/package/vaderjs)
15
15
 
16
16
 
17
-
17
+ > Do not use any alpha versions as these where changed multiple times any version under latest is considered lts and are deemed to be stable
18
18
  ## Get Started
19
19
 
20
20
  2. Install vaderjs
@@ -81,7 +81,7 @@ export default function(req, res){
81
81
 
82
82
  return <>
83
83
  <h1>${count}</h1>
84
- <button onClick={(count, setCount)=>{setCount(count + 1)}}>
84
+ <button onClick={(event)=>{setCount(count + 1)}}>
85
85
  </>
86
86
  }
87
87
 
@@ -126,9 +126,11 @@ export function Layout({title, keywords, description, children}){
126
126
 
127
127
  // pages/index.jsx
128
128
 
129
+ //$= is a ternary operator used for spread like nesting
130
+
129
131
  export default function (req, res){
130
132
  return <>
131
- <Layout {...{title:'home', description:'home page', keywords:'vader.js', logo:''}}>
133
+ <Layout $={{title:'home', description:'home page', keywords:'vader.js', logo:''}}>
132
134
  <h1> Hello World</h1>
133
135
  </Layout>
134
136
  </>
@@ -173,6 +175,8 @@ export default class MyApp extends Component{
173
175
  contructor(){
174
176
  super()
175
177
  this.key = 'static key for state changes'
178
+ // or
179
+ this.nokey // disable element generation
176
180
  }
177
181
 
178
182
  render(){
@@ -215,7 +219,7 @@ return <>
215
219
  </>
216
220
  ```
217
221
 
218
- and lower level invokes - these operate the same just allow you to pass items from top level to lower level: ex - I have a variable named car and i want the button to log it i can pass it as a parameter to allow it to be added to the buttons function scope
222
+ Low level invokes are considered top level and can access - any value above the scope !!
219
223
 
220
224
  ```jsx
221
225
  let car = {
@@ -223,7 +227,7 @@ let car = {
223
227
  price: 'toomiuch'
224
228
  }
225
229
  return <>
226
- <button onclick={(car)=>{
230
+ <button onclick={(event)=>{
227
231
  console.log(car.model)
228
232
  }}>Log</button>
229
233
  ```
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-5b5a772ebe58",
5
+ "version": "1.3.3-7924566dd811",
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)),window.onpopstate=async e=>{let t=window.location.pathname,r=`/${t.split("/")[1]}`;console.log("route",t),this.checkroute(t)||window.devMode||(t="/404");let n=(new DOMParser).parseFromString(await fetch(r,{cache:"reload"}).then((e=>e.text())),"text/html").documentElement;document.querySelector("#root").innerHTML=n.querySelector("#root").innerHTML,document.title=n.querySelector("title")?n.querySelector("title").innerHTML:document.title,document.querySelector('script[id="router"]').remove();let o=document.createElement("script");o.id="router",o.innerHTML=n.querySelector('script[id="router"]').innerHTML,o.setAttribute("type","module"),document.body.appendChild(o)},window.rehydrate=async()=>{window.location.reload()},this.listeners.push(e),1===this.listeners.length?this.handleRoute(window.location.pathname):this.listeners.pop(),t&&t()}extractParams(e,t){const r=e.split("/").filter((e=>""!==e)),n=t.split("/").filter((e=>""!==e)),o={};return r.forEach(((e,t)=>{if(e.startsWith(":")){const r=e.slice(1);o[r]=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 r={};return t.split("&").forEach((e=>{const[t,n]=e.split("=");r[t]=n})),r}checkroute(e){return e=e.endsWith("/")?e.slice(0,-1):e,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 r=t.path.split("/").filter((e=>""!==e)),n=e.split("/").filter((e=>""!==e));if(r.length!==n.length&&!t.path.endsWith("*"))return!1;for(let e=0;e<r.length;e++){const t=r[e],o=n[e];if(!t.startsWith(":")&&!t.startsWith("*")&&t!==o)return!1}return!0}const r=this.extractParams(t.path,e);return Object.keys(r).length>0}))}handleRoute(e){let t=200,r=e,n=this.checkroute(e);n||(n=window.routes.find((e=>!e.url.includes("/404")||this.error||window.devMode?!(this.error||!e.url.includes("/404"))||void 0:(window.history.pushState({},"","/404"),window.dispatchEvent(new Event("popstate")),this.error=!0,!1))),t=n?200:404);const o=this.extractQueryParams(r),s=n&&n.path?this.extractParams(n.path,r):{};Object.keys(s).forEach((e=>{s[e]=s[e].split("?")?s[e].split("?")[0]:s[e]}));const i={headers:{},params:s,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.pathname)},redirect:e=>{!e.startsWith("/")&&(e=`/${e}`),window.history.pushState({},"",e),window.dispatchEvent(new Event("popstate"))},render:async(e,t,r,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,r]),request:t,response:r,params:s,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}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=r,n.router.use&&!n.isChild?await new Promise((async o=>{if(i(e.default))if(i(e.default))switch(await n.router.use(t,r),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,r]),await n.router.use(t,r),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&&(document.querySelector("#root").innerHTML=a),n.bindMount(),n.onMount()}catch(e){console.error(e)}},setQuery:e=>{let t="";Object.keys(e).forEach(((r,n)=>{t+=`${0===n?"?":"&"}${r}=${e[r]}`}));let r=window.location.hash.split("?")[0];t=t.replace("/","-").replaceAll("/","-"),window.location.hash=`${r}${t}`},send:e=>{document.querySelector("#root").innerHTML=e},json:e=>{const t=document.querySelector("#root");t.innerHTML="";const r=document.createElement("pre");r.textContent=JSON.stringify(e,null,2),t.appendChild(r)}};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}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)),window.onpopstate=async e=>{let t=window.location.pathname,r=`/${t.split("/")[1]}`;console.log("route",t),this.checkroute(t)||window.devMode||(t="/404");let n=(new DOMParser).parseFromString(await fetch(r,{cache:"reload"}).then((e=>e.text())),"text/html").documentElement;document.querySelector("#root").innerHTML=n.querySelector("#root").innerHTML,document.title=n.querySelector("title")?n.querySelector("title").innerHTML:document.title,document.querySelector('script[id="router"]').remove();let o=document.createElement("script");o.id="router",o.innerHTML=n.querySelector('script[id="router"]').innerHTML,o.setAttribute("type","module"),document.body.appendChild(o)},window.rehydrate=async()=>{window.location.reload()},this.listeners.push(e),1===this.listeners.length?this.handleRoute(window.location.pathname):this.listeners.pop(),t&&t()}extractParams(e,t){const r=e.split("/").filter((e=>""!==e)),n=t.split("/").filter((e=>""!==e)),o={};return r.forEach(((e,t)=>{if(e.startsWith(":")){const r=e.slice(1);o[r]=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 r={};return t.split("&").forEach((e=>{const[t,n]=e.split("=");r[t]=n})),r}checkroute(e){return e=e.endsWith("/")?e.slice(0,-1):e,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 r=t.path.split("/").filter((e=>""!==e)),n=e.split("/").filter((e=>""!==e));if(r.length!==n.length&&!t.path.endsWith("*"))return!1;for(let e=0;e<r.length;e++){const t=r[e],o=n[e];if(!t.startsWith(":")&&!t.startsWith("*")&&t!==o)return!1}return!0}const r=this.extractParams(t.path,e);return Object.keys(r).length>0}))}handleRoute(e){let t=200,r=e,n=this.checkroute(e);n||(n=window.routes.find((e=>!e.url.includes("/404")||this.error||window.devMode?!(this.error||!e.url.includes("/404"))||void 0:(window.history.pushState({},"","/404"),window.dispatchEvent(new Event("popstate")),this.error=!0,!1))),t=n?200:404);const o=this.extractQueryParams(r),s=n&&n.path?this.extractParams(n.path,r):{};Object.keys(s).forEach((e=>{s[e]=s[e].split("?")?s[e].split("?")[0]:s[e]}));const i={headers:{},params:s,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.pathname)},redirect:e=>{!e.startsWith("/")&&(e=`/${e}`),window.history.pushState({},"",e),window.dispatchEvent(new Event("popstate"))},render:async(e,t,r,n)=>{function isClass(e){return"function"==typeof e&&/^class\s/.test(Function.prototype.toString.call(e))}try{let n=new Component;if(isClass(e.default)){let t=new e.default;n.state=t.state,n=t}else{e.default.toString();n.key=e.default.toString().split('key="')[1]?e.default.toString().split('key="')[1].split('"')[0]:null;let i=e.default.toString().split("this.key")[1]?e.default.toString().split("this.key")[1].split("=")[1].split(";")[0].trim().replaceAll('"',""):null;i&&(n.key=i),console.log("key",n.key);let a={key:n.key,render:()=>{let o=document.createElement("div");return n.key&&o.setAttribute("key",n.key),o.innerHTML=e.default.apply(n,[t,r]),o.outerHTML},request:t,response:r,params:s,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=a.render,n=a}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=r,n.router.use&&!n.isChild?await new Promise((async o=>{if(isClass(e.default))if(isClass(e.default))switch(await n.router.use(t,r),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,r]),await n.router.use(t,r),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 i=await n.render();document.querySelector("#root").innerHTML!==i&&(document.querySelector("#root").innerHTML=i),n.bindMount(),n.onMount()}catch(e){console.error(e)}},setQuery:e=>{let t="";Object.keys(e).forEach(((r,n)=>{t+=`${0===n?"?":"&"}${r}=${e[r]}`}));let r=window.location.hash.split("?")[0];t=t.replace("/","-").replaceAll("/","-"),window.location.hash=`${r}${t}`},send:e=>{document.querySelector("#root").innerHTML=e},json:e=>{const t=document.querySelector("#root");t.innerHTML="";const r=document.createElement("pre");r.textContent=JSON.stringify(e,null,2),t.appendChild(r)}};middlewares.forEach((e=>{e(i,a)})),n&&n.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 s=setInterval((()=>{document.querySelector(`[key="${e}"]`)&&!hasRan.includes(e)&&(clearInterval(s),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,s){function r(e){return"function"==typeof e&&/^class\s/.test(Function.prototype.toString.call(e))}let n=r(e)?new e(t):null;if(!e)throw new Error("Component must be defined");let i=new Component(t);if(r(e))n.props=t||{},n.props.children=s.join("")||[],n.props.children=s.join(""),n.parentNode=this,n.request=this.request,n.response=this.response,n.key=n.props.key?n.props.key:Math.random(),i=n;else{e.toString();i.key=e.toString().split('key="')[1]?e.toString().split('key="')[1].split('"')[0]:null;let r=[];Object.keys(t).forEach((e=>{e.startsWith("$props")&&(r.push(t[e]),delete t[e])})),t=t?{...t,...r.reduce(((e,t)=>({...e,...t})),{}),children:s.join("")||[]}:{children:s.join("")||[]};let n={key:i.key?i.key:Math.random(),isUnique:!!i.key,render:()=>e.apply(i,[t]),request:this.request,isChild:!0,response:this.response,params:this.request.params,queryParams:this.request.query,reset:i.reset.bind(i),onMount:i.onMount.bind(i),useState:null,router:{use:i.router.use.bind(i)},bindMount:i.bindMount.bind(i),memoize:i.memoize.bind(i),createComponent:i.createComponent.bind(i),isChild:!1,useState:i.useState.bind(i),parseStyle:i.parseStyle.bind(i),bind:i.bind.bind(i),useRef:i.useRef.bind(i),request:this.request,response:this.response,useReducer:i.useReducer.bind(i),hydrate:i.hydrate.bind(i),onUnmount:i.onUnmount.bind(i),parentNoe:this,props:{...t,children:s.join("")||[]}};i.render=n.render,i=n,i.props.children=s.join("")||[]}return this.components[i.key]||(this.components[i.key]=i),!this.children.includes(i)&&this.children.push(i),this.components[i.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 s=t.render();return s&&s.split(">,").length>1&&(s=s.replaceAll(">,",">")),`<span key="${e.key}" >${s}</span>`}parseStyle(e){let t="";return Object.keys(e).forEach((s=>{let r=e[s];s=s.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g,"$1-$2").toLowerCase(),t+=`${s}:${r};`})),t}bindMount(){mounts.push(this)}domDifference(e,t){let s=[];for(let r=0;r<e.length;r++){let n=e[r],i=t[r];n&&i&&!n.isEqualNode(i)&&s.push({type:"replace",old:n,new:i.cloneNode(!0)})}return s}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("hydrating");let t=(new DOMParser).parseFromString(this.render(),"text/html").body.querySelector(`[ref="${e}"]`);document.querySelector(`[ref="${e}"]`);document.querySelector(`[ref="${e}"]`).replaceWith(t)}else{let e=this.key?document.querySelector(`[key="${this.key}"]`):null;e&&(e.innerHTML=this.render())}}patch(e,t){const s=this.domDifference(e,t);this.updateChangedElements(s)}handleObject(obj){try{obj=JSON.parse(obj)}catch(e){}return eval(obj)}bind(e,t,s,r,...n){return s+=this.key,window["callFunctions"+this.key]=(e,t)=>{console.log("called");let s=this.functions.find((t=>t.ref===e));s&&s.func(t)},this.functions.find((e=>e.ref===s))||this.functions.push({ref:s,func:e}),t?e:`((event)=>{callFunctions${this.key} ? callFunctions${this.key}('${s}', event) : null})(event)`}useState(e,t){this.state.hasOwnProperty(e)||(this.state[e]=t);return[(()=>this.state[e])(),(t,s)=>{this.state[e]=t,this.hydrate(s)}]}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,s=null){this.state[e]||(this.state[e]=t);const r=()=>this.state[e];let n=r();return[r(),(t,i)=>{const o=s(n,t)??t;this.state[e]=o,this.hydrate(i),n=r()}]}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)=>{this.state[e]||(this.state[e]=t);return[states[e],(t,s)=>{states[e]=t,this.hydrate(s)}]};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"),this.key=e.href+Math.random()}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 class Image extends Component{constructor(e){super(e),this.props={src:e.src,class:e.class,style:e.style,blur:e.blur,width:e.width,height:e.height,optimize:e.optimize||!0,loader:e.loader||!0,alt:e.alt||"image",ref:e.ref||null},this.key=e.src+Math.random(),this.img=document.createElement("img"),this.placeholder=document.createElement("div")}render(){if(window.isServer)return"";let[e,t]=this.useState("loaded",!1),s=this.useRef("hookref",null),r=this.props.width?this.props.width:window.innerWidth/2,n=this.props.height?this.props.height:window.innerHeight/2;if(!this.props.src)throw new Error("Image src is required");return this.img.setAttribute("src",this.props.src),this.img.setAttribute("class",this.props.class),this.img.setAttribute("style",this.props.style?this.props.style:""),this.img.setAttribute("width",r),this.img.setAttribute("ref",s.bind),this.img.referrerPolicy="no-referrer",this.img.setAttribute("height",n),this.img.setAttribute("loading","lazy"),this.img.setAttribute("alt",this.props.alt),this.props.blur&&this.img.setAttribute("style",`filter: blur(${this.props.blur}px);`),this.props.optimize&&this.img.setAttribute("style",`image-rendering: -webkit-optimize-contrast; object-fit: cover; object-position: center; ${this.props.style?this.props.style:""}`),!this.props.loader||e||window.isServer||(this.placeholder.setAttribute("style",`width: ${r}px; height: ${n}px; background: #eee;`),this.placeholder.setAttribute("class","vader-image-placeholder"),this.placeholder.innerHTML=this.props.loader,window.isServer)?void 0:(this.img.onload=()=>{t(!0,s.bind)},`<span ref="${s.bind}">${e?this.img.outerHTML:this.placeholder.outerHTML}</span>`)}}export class Html extends Component{constructor(e){super(e),this.props={children:e.children,lang:e.lang||"en",attributes:e.attributes||{}},this.key="html",this.html=document.createElement("div")}render(){return window.isServer?(this.html.innerHTML=this.props.children,this.html.setAttribute("lang",this.props.lang?this.props.lang:"en"),this.props.attributes&&Object.keys(this.props.attributes).forEach((e=>{this.html.setAttribute(e,this.props.attributes[e])})),this.html.innerHTML):this.props.children}onMount(){console.log("Document Has Been Mounted")}}export class Head extends Component{constructor(e){super(e),this.props=e,this.key="head",this.head=document.createElement("head")}render(){return""}onMount(){if(!this.state.hasMounted&&window.isServer){document.head.innerHTML=this.props.children+document.head.innerHTML,document.querySelectorAll("script[eager]").forEach((async e=>{if(!e.getAttribute("src"))throw new Error("Eager scripts must be external");e.remove();let t=e.getAttribute("src"),s=document.createElement("script"),r=await fetch(t).then((e=>e.text()));s.innerHTML=r,s.setAttribute("srcid",t),document.querySelector(`[srcid="${t}"]`)||document.head.prepend(s)})),this.state.hasMounted=!0}}}export class Script extends Component{constructor(e){super(e),this.props={children:e.children},this.key="script",this.script=document.createElement("script")}render(){return this.script.innerHTML=this.props.children.split("\n").join(";\n"),this.script.outerHTML}onMount(){document.head.appendChild(this.script),document.body.querySelector(`[key="${this.key}"]`).remove()}}export default{Component:Component,useRef:useRef,useReducer:useReducer,useState:useState,strictMount:strictMount,Link:Link,Image:Image,Head:Head,Script:Script,Html:Html};
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 s=setInterval((()=>{document.querySelector(`[key="${e}"]`)&&!hasRan.includes(e)&&(clearInterval(s),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,s){function isClass(e){return"function"==typeof e&&/^class\s/.test(Function.prototype.toString.call(e))}let r=isClass(e)?new e(t):null;if(!e)throw new Error("Component must be defined");let i=new Component(t);if(isClass(e))r.props=t||{},r.props.children=s.join("")||[],r.props.children=s.join(""),r.parentNode=this,r.request=this.request,r.response=this.response,r.key=r.props.key?r.props.key:Math.random(),i=r;else{e.toString();i.key=e.toString().split('key="')[1]?e.toString().split('key="')[1].split('"')[0]:null;let r=[];Object.keys(t).forEach((e=>{if(e.startsWith("$props")&&(r.push(t[e]),delete t[e]),e.startsWith("$_ternary")){let s=t[e];delete t[e],Object.keys(s).forEach((e=>{t[e]=s[e]}))}})),t=t?{...t,...r.reduce(((e,t)=>({...e,...t})),{}),children:s.join("")||[]}:{children:s.join("")||[]};let n={key:i.key?i.key:Math.random(),isUnique:!!i.key,render:()=>e.apply(i,[t]),request:this.request,isChild:!0,response:this.response,params:this.request.params,queryParams:this.request.query,reset:i.reset.bind(i),onMount:i.onMount.bind(i),useState:null,router:{use:i.router.use.bind(i)},bindMount:i.bindMount.bind(i),memoize:i.memoize.bind(i),createComponent:i.createComponent.bind(i),isChild:!1,useState:i.useState.bind(i),parseStyle:i.parseStyle.bind(i),bind:i.bind.bind(i),useRef:i.useRef.bind(i),request:this.request,response:this.response,useReducer:i.useReducer.bind(i),hydrate:i.hydrate.bind(i),onUnmount:i.onUnmount.bind(i),parentNoe:this,props:{...t,children:s.join("")||[]}};i.render=n.render,i=n,i.props.children=s.join("")||[]}return this.components[i.key]||(this.components[i.key]=i),!this.children.includes(i)&&this.children.push(i),this.components[i.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 s=t.render();return s&&s.split(">,").length>1&&(s=s.replaceAll(">,",">")),t.nokey?s:`<div key="${t.key}">${s}</div>`}parseStyle(e){let t="";return Object.keys(e).forEach((s=>{let r=e[s];s=s.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g,"$1-$2").toLowerCase(),t+=`${s}:${r};`})),t}bindMount(){mounts.push(this)}domDifference(e,t){let s=[];for(let r=0;r<e.length;r++){let i=e[r],n=t[r];i&&n&&!i.isEqualNode(n)&&s.push({type:"replace",old:i,new:n.cloneNode(!0)})}return s}updateChangedElements(e){e.forEach((e=>{switch(e.type){case"replace":e.old.replaceWith(e.new);break;case"remove":e.old.remove();break;case"add":e.old.appendChild(e.new.cloneNode(!0))}}))}hydrate(e){if(e){console.log("hydrating");let t=(new DOMParser).parseFromString(this.render(),"text/html").body.querySelector(`[ref="${e}"]`);document.querySelector(`[ref="${e}"]`);document.querySelector(`[ref="${e}"]`).replaceWith(t)}else{if(this.key?document.querySelector(`[key="${this.key}"]`):null){let e=(new DOMParser).parseFromString(this.render(),"text/html").body.querySelector(`[key="${this.key}"]`),t=document.querySelector(`[key="${this.key}"]`),s=this.domDifference(t.children,e.children);this.updateChangedElements(s)}}}patch(e,t){const s=this.domDifference(e,t);this.updateChangedElements(s)}handleObject(obj){try{obj=JSON.parse(obj)}catch(e){}return eval(obj)}bind(e,t,s,r,...i){return s+=this.key,window["callFunctions"+this.key]=(e,t)=>{console.log("called");let s=this.functions.find((t=>t.ref===e));s&&s.func(t)},this.functions.find((e=>e.ref===s))||this.functions.push({ref:s,func:e}),t?e:`((event)=>{callFunctions${this.key} ? callFunctions${this.key}('${s}', event) : null})(event)`}setState(e,t){this.state=e,this.hydrate(t)}useState(e,t){this.state.hasOwnProperty(e)||(this.state[e]=t);return[(()=>this.state[e])(),(t,s)=>{this.state[e]=t,this.hydrate(s)}]}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,s=null){this.state[e]||(this.state[e]=t);const getValue=()=>this.state[e];let r=getValue();return[getValue(),(t,i)=>{const n=s(r,t)??t;this.state[e]=n,this.hydrate(i),r=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)=>{this.state[e]||(this.state[e]=t);return[states[e],(t,s)=>{states[e]=t,this.hydrate(s)}]};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"),this.key=e.href+Math.random(),this.nokey=!0}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 class Image extends Component{constructor(e){super(e),this.props={src:e.src,class:e.class,style:e.style,blur:e.blur,width:e.width,height:e.height,optimize:e.optimize||!0,loader:e.loader||!0,alt:e.alt||"image",ref:e.ref||null},this.key=e.src+Math.random(),this.img=document.createElement("img"),this.placeholder=document.createElement("div")}render(){if(window.isServer)return"";let[e,t]=this.useState("loaded",!1),s=this.useRef("hookref",null),r=this.props.width?this.props.width:window.innerWidth/2,i=this.props.height?this.props.height:window.innerHeight/2;if(!this.props.src)throw new Error("Image src is required");return this.img.setAttribute("src",this.props.src),this.img.setAttribute("class",this.props.class),this.img.setAttribute("style",this.props.style?this.props.style:""),this.img.setAttribute("width",r),this.img.setAttribute("ref",s.bind),this.img.referrerPolicy="no-referrer",this.img.setAttribute("height",i),this.img.setAttribute("loading","lazy"),this.img.setAttribute("alt",this.props.alt),this.props.blur&&this.img.setAttribute("style",`filter: blur(${this.props.blur}px);`),this.props.optimize&&this.img.setAttribute("style",`image-rendering: -webkit-optimize-contrast; object-fit: cover; object-position: center; ${this.props.style?this.props.style:""}`),!this.props.loader||e||window.isServer||(this.placeholder.setAttribute("style",`width: ${r}px; height: ${i}px; background: #eee;`),this.placeholder.setAttribute("class","vader-image-placeholder"),this.placeholder.innerHTML=this.props.loader,window.isServer)?void 0:(this.img.onload=()=>{t(!0,s.bind)},`<span ref="${s.bind}">${e?this.img.outerHTML:this.placeholder.outerHTML}</span>`)}}export class Html extends Component{constructor(e){super(e),this.props={children:e.children,lang:e.lang||"en",attributes:e.attributes||{}},this.key="html",this.html=document.createElement("div")}render(){return window.isServer?(this.html.innerHTML=this.props.children,this.html.setAttribute("lang",this.props.lang?this.props.lang:"en"),this.props.attributes&&Object.keys(this.props.attributes).forEach((e=>{this.html.setAttribute(e,this.props.attributes[e])})),this.html.innerHTML):this.props.children}onMount(){console.log("Document Has Been Mounted")}}export class Head extends Component{constructor(e){super(e),this.props=e,this.key="head",this.head=document.createElement("head")}render(){return""}onMount(){if(!this.state.hasMounted&&window.isServer){document.head.innerHTML=this.props.children+document.head.innerHTML,document.querySelectorAll("script[eager]").forEach((async e=>{if(!e.getAttribute("src"))throw new Error("Eager scripts must be external");e.remove();let t=e.getAttribute("src"),s=document.createElement("script"),r=await fetch(t).then((e=>e.text()));s.innerHTML=r,s.setAttribute("srcid",t),document.querySelector(`[srcid="${t}"]`)||document.head.prepend(s)})),this.state.hasMounted=!0}}}export class Script extends Component{constructor(e){super(e),this.props={children:e.children},this.key="script",this.script=document.createElement("script")}render(){return this.script.innerHTML=this.props.children.split("\n").join(";\n"),this.script.outerHTML}onMount(){document.head.appendChild(this.script),document.body.querySelector(`[key="${this.key}"]`).remove()}}export default{Component:Component,useRef:useRef,useReducer:useReducer,useState:useState,strictMount:strictMount,Link:Link,Image:Image,Head:Head,Script:Script,Html:Html};
package/vader.js CHANGED
@@ -22,16 +22,8 @@ let writer = async (file, data) => {
22
22
  globalThis.isWriting = null
23
23
  return { _written: true };
24
24
  };
25
-
26
- let start = Date.now()
27
- let bundleSize = 0;
28
- let errorCodes = {
29
- "SyntaxError: Unexpected token '<'": "You forgot to enclose tags in a fragment <></>",
30
- }
31
- /**
32
- * define directories
33
- */
34
-
25
+
26
+ let bundleSize = 0;
35
27
 
36
28
  if (!fs.existsSync(process.cwd() + '/dist')) {
37
29
  fs.mkdirSync(process.cwd() + '/dist')
@@ -48,44 +40,13 @@ if (typeof process.env.isCloudflare !== "undefined" || !fs.existsSync(process.cw
48
40
 
49
41
 
50
42
  function Compiler(func, file) {
51
- let string = func;
52
- // Remove block comments
53
-
43
+ let string = func;
54
44
  let returns = []
55
45
  let comments = string.match(/\{\s*\/\*.*\*\/\s*}/gs)?.map((comment) => comment.trim());
56
46
 
57
- let savedfuncnames = [];
58
- let functions = string.match(
59
- /(?:const|let)\s*([a-zA-Z0-9_-]+)\s*=\s*function\s*\(([^)]*)\)|function\s*([a-zA-Z0-9_-]+)\s*\(([^)]*)\)/gs
60
- )
61
- ?.map((match) => match.trim());
62
-
63
- let functionNames = [];
64
-
65
-
66
- functions && functions.forEach((func) => {
67
- if (
68
- !func.match(
69
- /(?:const|let)\s*([a-zA-Z0-9_-]+)\s*=\s*function\s*\(([^)]*)\)|function\s*([a-zA-Z0-9_-]+)\s*\(([^)]*)\)/gs
70
- )
71
- ) {
72
- return;
73
- }
74
-
75
- let name = func.split(" ")[1].split("(")[0].trim();
76
-
77
- let lines = string.match(/return\s*\<>.*\<\/>/gs);
78
-
79
- if (lines) {
80
- for (let i = 0; i < lines.length; i++) {
81
- let line = lines[i];
82
-
83
- if (!functionNames.includes(name)) {
84
- functionNames.push(name);
85
- }
86
- }
87
- }
88
- });
47
+
48
+
49
+
89
50
 
90
51
  // get all Obj({}) and parse to JSON.stringify
91
52
 
@@ -101,6 +62,51 @@ function Compiler(func, file) {
101
62
 
102
63
  let childs = [];
103
64
 
65
+ const spreadAttributeRegex = /\s*([a-zA-Z0-9_-]+)(\s*\$\s*=\s*{{((?:[^{}]|(?:\{(?:[^{}]|(?:\{[^{}]*\})*)*\})*)*)}})/gs;
66
+ let spreadMatch;
67
+ while ((spreadMatch = spreadAttributeRegex.exec(string)) !== null) {
68
+ let [, element, spread] = spreadMatch;
69
+ let isJSXComponent = element.match(/[A-Z]/) ? true : false;
70
+ if (isJSXComponent) {
71
+ continue
72
+
73
+ }
74
+ let old = spread;
75
+
76
+ // turn spread into attributes
77
+ spread = spread.replace(/\s*$\s*=\s*/, "");
78
+ spread = spread.replace(/{{/, "");
79
+ spread = spread.replace(/}}/, "");
80
+ spread = spread.replace(/\$\s*=\s*/, "");
81
+
82
+ // turn : into =
83
+
84
+ // do not split inner Objects ex: {color: 'red', background: {color: 'blue'}} -> {color: 'red', background: {color: 'blue'}}
85
+ let splitByCommas = spread.split(/,(?![^{]*})/g);
86
+ splitByCommas = splitByCommas.map((e) => e.trim())
87
+ splitByCommas = splitByCommas.map((e) => {
88
+ switch (true) {
89
+ case e.includes('function') || e.includes('=>'):
90
+ e = e.replace(/:(.*)/gs, '={$1}')
91
+ break;
92
+ case e.includes('style'):
93
+ e = e.replace(/:(.*)/gs, '="${this.parseStyle($1)}"')
94
+ break;
95
+ case e.includes('[') && e.includes(']'):
96
+ e = e.replace(/:(.*)/gs, '={$1.join(" ")}')
97
+ break;
98
+ default:
99
+ }
100
+
101
+ return e.trim()
102
+ })
103
+
104
+ let newSpread = `\t` + splitByCommas.join(' ') + `\t`
105
+
106
+ string = string.replace(old, newSpread);
107
+
108
+ }
109
+
104
110
 
105
111
 
106
112
  function extractAttributes(code) {
@@ -112,41 +118,42 @@ function Compiler(func, file) {
112
118
  const attributeRegex =
113
119
  /\s*([a-zA-Z0-9_-]+)(\s*=\s*("([^"\\]*(\\.[^"\\]*)*)"|'([^'\\]*(\\.[^'\\]*)*)'|\{(?:[^{}]|(?:\{(?:[^{}]|(?:\{[^{}]*\})*)*\})*)*\}|(?:\([^)]*\)|\{[^}]*\}|()=>\s*(?:\{[^}]*\})?)|\[[^\]]*\]))?/gs;
114
120
 
115
- // only return elements with attribute {()=>{}} or if it also has parameters ex: onclick={(event)=>{console.log(event)}} also get muti line functions
121
+ // <div $={{color: 'red'}}></div>
122
+
123
+
124
+
125
+ // only return elements with attribute {()=>{}} or if it also has parameters ex: onclick={(event)=>{console.log(event)}} also get muti line functions or onClick=()=>{}
116
126
  const functionAttributeRegex = /\s*([a-zA-Z0-9_-]+)(\s*=\s*{((?:[^{}]|(?:\{(?:[^{}]|(?:\{[^{}]*\})*)*\})*)*)})/gs;
117
127
 
118
128
  let attributesList = [];
119
129
 
120
- // handle functions
130
+ let spreadAttributes = [];
131
+ let spreadMatch;
132
+ /**
133
+ * @search - handle spread for html elements
134
+ * @keywords - spread, spread attributes, spread props, spread html attributes
135
+ */
136
+
137
+
138
+ /**
139
+ * @search - handle function parsing for html elements
140
+ * @keywords - function, function attributes, function props, function html attributes
141
+ *
142
+ */
121
143
  let functionAttributes = [];
122
144
  let functionMatch;
123
145
  while ((functionMatch = functionAttributeRegex.exec(code)) !== null) {
124
- let [, attributeName, attributeValue] = functionMatch;
125
146
 
147
+ let [, attributeName, attributeValue] = functionMatch;
126
148
  let attribute = {};
127
149
 
128
- if (attributeValue && attributeValue.includes("=>") || attributeValue && attributeValue.includes("function")) {
129
- let functionparams = [];
130
- // ref with no numbers
150
+ if (attributeValue && attributeValue.includes("=>") || attributeValue && attributeValue.includes("function")
151
+ && !spreadFunctions.includes(attributeValue)
152
+ ) {
153
+
131
154
  let ref = Math.random().toString(36).substring(2).split('').filter((e) => !Number(e)).join('')
132
155
  let old = `${attributeName}${attributeValue}`
133
- functionNames.forEach((name) => {
134
- string.split("\n").forEach((line) => {
135
- if (line.includes(name) && line.includes("function")) {
136
- line = line.trim();
137
- line = line.replace(/\s+/g, " ");
138
-
139
- let ps = line.split("(").slice(1).join("(").split(")")[0].trim();
140
-
141
- // remove comments
142
- ps = ps.match(/\/\*.*\*\//gs)
143
- ? ps.replace(ps.match(/\/\*.*\*\//gs)[0], "")
144
- : ps;
145
- functionparams.push({ ref: ref, name: name, params: ps });
146
-
147
- }
148
- });
149
- });
156
+
150
157
  let elementMatch = string.match(/<([a-zA-Z0-9_-]+)([^>]*)>/gs);
151
158
  let isJSXComponent = false;
152
159
  elementMatch.forEach((element) => {
@@ -200,8 +207,7 @@ function Compiler(func, file) {
200
207
  newvalue = newvalue.replace(/}\s*$/, '');
201
208
 
202
209
 
203
-
204
- functionparams.length > 0 ? params = params + ',' + functionparams.map((e) => e.name).join(',') : null
210
+
205
211
 
206
212
  newvalue = newvalue.replaceAll(',,', ',')
207
213
  let paramnames = params ? params.split(',').map((e) => e.trim()) : null
@@ -224,6 +230,10 @@ function Compiler(func, file) {
224
230
  }
225
231
  }
226
232
 
233
+ /**
234
+ * @search - handle attributes for html elements
235
+ * @keywords - attributes, props, html attributes
236
+ */
227
237
  let match;
228
238
  while ((match = elementRegex.exec(code)) !== null) {
229
239
  let [, element, attributes] = match;
@@ -248,8 +258,7 @@ function Compiler(func, file) {
248
258
  let returns = code.match(/return\s*\<>.*\<\/>|return\s*\(.*\)/gs);
249
259
 
250
260
  return returns || [];
251
- }
252
- // throw error if return is not wrapped in <></> or
261
+ }
253
262
  if (string.match(/return\s*\<>|return\s*\(.*\)/gs) && !string.match(/return\s*\<>.*\<\/>|return\s*\(.*\)/gs)
254
263
  || string.match(/return\s*\<[a-zA-Z0-9_-]+.*>/gs)
255
264
  ) {
@@ -265,6 +274,10 @@ function Compiler(func, file) {
265
274
  let outerReturn = extractOuterReturn(string);
266
275
  let contents = "";
267
276
  let updatedContents = "";
277
+ /**
278
+ * @search - handle return [...]
279
+ * @keywords - return, return jsx, return html, return [...]
280
+ */
268
281
  outerReturn.forEach((returnStatement) => {
269
282
 
270
283
  let lines = returnStatement.split("\n");
@@ -278,13 +291,12 @@ function Compiler(func, file) {
278
291
  }
279
292
  let usesBraces = returnStatement.match(/return\s*\(/gs) ? true : false;
280
293
 
281
- let attributes = extractAttributes(contents);
282
- // Remove trailing ']' or trailing )
294
+
283
295
  contents = contents.trim().replace(/\]$/, "")
284
296
  contents = contents.replace(/\)$/, "");
285
297
  usesBraces ? !contents.includes('<>') ? contents = `<>${contents}</>` : null : null
286
298
  updatedContents = contents;
287
-
299
+ let attributes = extractAttributes(contents);
288
300
 
289
301
  let newAttributes = [];
290
302
  let oldAttributes = [];
@@ -363,9 +375,7 @@ function Compiler(func, file) {
363
375
  valuestate = valuestate.match(regex) ? valuestate.match(regex)[0].split("useState(")[1].split(")")[0].trim() : valuestate
364
376
 
365
377
 
366
- let newState = `${varType} [${key}, ${setKey}] = this.useState('${key}', ${valuestate}
367
-
368
- `;
378
+ let newState = `${varType} [${key}, ${setKey}] = this.useState('${key}', ${valuestate}`;
369
379
  string = string.replace(line, newState);
370
380
  break;
371
381
  case line.includes("useRef") && !line.includes("import"):
@@ -410,25 +420,24 @@ function Compiler(func, file) {
410
420
  string = string.replaceAll('../src', './src')
411
421
 
412
422
  function parseComponents(body, isChild) {
413
- let componentRegex = /<([A-Z][A-Za-z0-9_-]+)([^>]*)>(.*?)<\/\1>|<([A-Z][A-Za-z0-9_-]+)([^]*?)\/>/gs;
423
+ let componentRegex = /<([A-Z][A-Za-z0-9_-]+)\s*([^>]*)>\s*([\s\S]*?)\s*<\/\1>|<([A-Z][A-Za-z0-9_-]+)([^]*?)\/>/gs;
414
424
 
415
425
  let componentMatch = body.match(componentRegex);
416
426
  let topComponent = "";
417
427
  componentMatch?.forEach(async (component) => {
418
428
 
419
429
  let [, element, attributes] = component;
420
-
430
+ let before = component;
431
+ component = component.trim().replace(/\s+/g, " ");
421
432
 
422
433
  !isChild ? (topComponent = component) : null;
423
- let before = component;
434
+
424
435
 
425
436
  let myChildrens = [];
426
437
 
427
438
  let name = component.split("<")[1].split(">")[0].split(" ")[0].replace("/", "");
428
- // some components will have props that have html inside of them we need to only get the props ex: <Header title={<h1>hello</h1>}></Header> -> title={<h1>hello</h1>} // also spread props ex: <Header {...props}></Header> -> {...props} or {...props, title: 'hello'} or {...props, color:{color: 'red'}}
429
- // grab ...( spread props )
430
- const dynamicAttributesRegex = /(\w+)(?:="([^"]*)")?(?:='([^']*)')?(?:=\{([^}]*)\})?(?:=\{(.*?)\})?(?:={([^}]*)})?(?:{([^}]*)})?|(?:{(?:[^{}]+|\{(?:[^{}]+|\{[^}]*\})*\})*\})|(\.{3}\{(?:[^{}]+|\{(?:[^{}]+|\{[^}]*\})*\})*\})/gs;
431
-
439
+ let componentAttributes = component.split("<")[1].split(">")[0].split(" ").join(" ").replace(name, "").trim();
440
+ const dynamicAttributesRegex = /(\w+)(?:="([^"]*)")?(?:='([^']*)')?(?:=\{([^}]*)\})?(?:=\{(.*?)\})?(?:={([^}]*)})?(?:{([^}]*)})?|(?:{(?:[^{}]+|\{(?:[^{}]+|\{[^}]*\})*\})*\})|(\.{3}\{(?:[^{}]+|\{(?:[^{}]+|\{[^}]*\})*\})*\})|\$=\{(?:[^{}]+|\{(?:[^{}]+|\{[^}]*\})*\})*\}/gs;
432
441
 
433
442
 
434
443
 
@@ -439,18 +448,31 @@ function Compiler(func, file) {
439
448
  let filteredProps = [];
440
449
  let isWithinComponent = false;
441
450
  let componentName = name
451
+ let currentProps = []
452
+
453
+ let $_ternaryprops = []
442
454
 
443
455
  for (let prop of props) {
444
456
 
445
457
  if (prop === componentName) {
446
-
447
- // If the component is encountered, start collecting props
458
+
448
459
  isWithinComponent = true;
449
460
  filteredProps.push(prop);
450
461
  } else if (isWithinComponent && prop.includes('=')) {
451
462
 
452
- if (prop.includes('${')) {
453
- // if it has an object inside of it then we should just do soemting:object else we should do something: `${object}`
463
+ if (prop.startsWith('$=')) {
464
+ let old = prop
465
+ prop = prop.replace('$=', '$_ternary=')
466
+
467
+ // remove trailing }
468
+ prop = prop.replace(/}\s*$/, '')
469
+ component = component.replace(old, prop)
470
+ componentAttributes = componentAttributes.replace(old, prop)
471
+
472
+ $_ternaryprops.push(prop)
473
+
474
+ }
475
+ else if (prop.includes('${')) {
454
476
 
455
477
  prop = prop.replace('="', ':').replace('}"', '}')
456
478
  if (prop.includes('${')) {
@@ -467,7 +489,7 @@ function Compiler(func, file) {
467
489
  }
468
490
 
469
491
  }
470
- if (prop.startsWith('={')) {
492
+ else if (prop.startsWith('={')) {
471
493
  prop = prop.replace('={', ':`${')
472
494
  prop.replace('} ', '}`')
473
495
  }
@@ -487,72 +509,25 @@ function Compiler(func, file) {
487
509
  filteredProps.push(prop);
488
510
 
489
511
 
490
- }
491
- else if (isWithinComponent && prop.includes('...')) {
492
-
493
-
494
-
495
- // Check if spread props are within curly braces
496
- if (prop.startsWith('{') && prop.endsWith('}')) {
497
- const spreadObject = prop
498
- const hasOtherObjects = spreadObject.split(',').filter((e) => e.includes(':')).length > 0;
499
-
500
- const isValidSpread = spreadObject.includes('...');
501
-
502
- let processedSpreadObject = '';
503
- if (isValidSpread) {
504
- // Split the spreadObject by commas and process each part individually
505
- const parts = spreadObject.split(',').map((part) => {
506
- if (part.trim().startsWith('{') && part.trim().endsWith('}')) {
507
- const nestedParts = part
508
- .trim()
509
- .slice(1, -1) // Remove outer {}
510
- .split(',')
511
- .map((nestedPart) => {
512
- return nestedPart.includes('...') ? nestedPart.trim().replace(/\.\.\./, '') : `...${nestedPart.trim()}`;
513
- });
514
- return `{${nestedParts.join(',')}}`;
515
- } else {
516
- return part.includes('...') ? part.trim() : part.trim().startsWith('{') ? `...${part.trim()}` : `${part.trim()}`;
517
- }
518
- });
519
- if (!parts.join(',').includes('{(')) {
520
-
521
- processedSpreadObject = `${parts.join(',')}`
522
512
 
523
- } else {
524
- let prop = parts.join(',')
525
- prop = prop.replaceAll('{(', '(')
526
- prop = prop.replaceAll(')}', ')')
527
- processedSpreadObject = prop
528
- }
529
- if (prop.includes('{{')) {
530
- let prop = parts.join(',')
531
- prop = prop.replaceAll('{{', '{')
532
- prop = prop.replaceAll('}}', '}')
533
- processedSpreadObject = prop
534
- }
535
-
536
-
537
- } else {
538
- // Process nested structures within the object
539
- processedSpreadObject = `{...${spreadObject}}`;
540
- }
541
-
542
- const $propsKey = `$props_${Math.random().toString(36).substring(2)}`;
543
- filteredProps.push(`${$propsKey}:${processedSpreadObject}`);
544
- }
545
513
  }
546
514
 
515
+
547
516
  else {
548
517
  isWithinComponent = false;
549
518
  }
550
519
  }
520
+ component = component.replaceAll(/\s+/g, " ");
521
+
522
+ component = component.replace(componentAttributes, '')
523
+ $_ternaryprops.forEach((prop) => {
524
+ component = component.replace(prop, '')
525
+ })
551
526
 
552
- // get inner content of <Component>inner content</Component>
553
- let children = new RegExp(`<${name}[^>]*>(.*?)<\/${name}>`, "gs").exec(component) ? new RegExp(`<${name}[^>]*>(.*?)<\/${name}>`, "gs").exec(component)[1] : null;
527
+ let children = component.split(`<${name}`)[1].split(`</${name}>`)[0].trim().replace(/\s+/g, " ").trim().replace(/,$/, '').replace('>', '').replace(/\/$/, '').trim()
554
528
 
555
- props = filteredProps.join(',')
529
+
530
+ props = filteredProps.join(',').replace(/\s+/g, " ").trim().replace(/,$/, '')
556
531
 
557
532
  let savedname = name;
558
533
 
@@ -573,12 +548,13 @@ function Compiler(func, file) {
573
548
  /<([A-Z][A-Za-z0-9_-]+)([^>]*)>(.*?)<\/\1>|<([A-Z][A-Za-z0-9_-]+)([^]*?)\/>/gs
574
549
  );
575
550
  if (html) {
576
- html = html.map((h) => h.trim().replace(/\s+/g, " ")).join(" ");
551
+ html = html.map((h) => h.trim().replace(/\s+/g, " ")).join(" ");
577
552
  child.children = child.children.replaceAll(html, `${html}`);
578
553
  // remove duplicate quotes
579
554
  }
580
555
 
581
556
  myChildrens.push(child.children);
557
+ childs = childs.filter((e) => e.parent !== name);
582
558
  }
583
559
  });
584
560
 
@@ -599,8 +575,10 @@ function Compiler(func, file) {
599
575
  * @memoize - memoize a component to be remembered on each render and replace the old jsx
600
576
  */
601
577
 
578
+
602
579
  let replace = "";
603
- replace = `\${this.memoize(this.createComponent(${savedname}, {${props}}, [\`${myChildrens.join(" ")}\`]))}`;
580
+ replace = `\${this.memoize(this.createComponent(${savedname}, {${props}}, [\`${myChildrens.join('')}\`]))}`;
581
+
604
582
 
605
583
  body = body.replace(before, replace);
606
584
  });
@@ -613,55 +591,56 @@ function Compiler(func, file) {
613
591
  const importRegex = /import\s*([^\s,]+|\{[^}]+\})\s*from\s*(['"])(.*?)\2/g;
614
592
  const imports = string.match(importRegex);
615
593
  let replaceMents = [];
616
-
617
-
618
- for (let match of imports) {
594
+
595
+
596
+ for (let match of imports) {
619
597
  let path = match.split('from')[1].trim().replace(/'/g, '').replace(/"/g, '').trim()
620
598
  switch (true) {
621
599
  case path && !path.includes('./') && !path.includes('/vader.js') && !path.includes('/vaderjs/client') && !path.startsWith('src') && !path.startsWith('public') && !path.includes('http') && !path.includes('https'):
622
- let componentFolder = fs.existsSync(process.cwd() + '/node_modules/' + path) ? process.cwd() + '/node_modules/' + path : process.cwd() + '/node_modules/' + path.split('/')[0]
623
- componentFolder = componentFolder.split(process.cwd())[1]
624
- if(!fs.existsSync(process.cwd() + componentFolder)){
600
+ let componentFolder = fs.existsSync(process.cwd() + '/node_modules/' + path) ? process.cwd() + '/node_modules/' + path : process.cwd() + '/node_modules/' + path.split('/')[0]
601
+ componentFolder = componentFolder.split(process.cwd())[1]
602
+ if (!fs.existsSync(process.cwd() + componentFolder)) {
625
603
  throw new Error('Could not find ' + path + ' at ' + match + ' in file ' + file)
626
- }
627
-
628
- if(!fs.existsSync(process.cwd() + '/dist/src/' + componentFolder.split('/').slice(2).join('/'))){
629
- fs.mkdirSync(process.cwd() + '/dist/src/' + componentFolder.split('/').slice(2).join('/'), { recursive: true })
604
+ }
605
+
606
+ if (!fs.existsSync(process.cwd() + '/dist/src/' + componentFolder.split('/').slice(2).join('/'))) {
607
+ fs.mkdirSync(process.cwd() + '/dist/src/' + componentFolder.split('/').slice(2).join('/'), { recursive: true })
608
+ }
609
+
610
+ let baseFolder = componentFolder.split('node_modules')[1].split('/')[1]
611
+ let glp = globSync('**/**/**/**.{jsx,js}', {
612
+ cwd: process.cwd() + '/node_modules/' + baseFolder + '/',
613
+ absolute: true,
614
+ recursive: true
615
+ })
616
+ for (let file of glp) {
617
+ let text = fs.readFileSync(file, "utf8");
618
+ if (!file.endsWith('.js') && file.endsWith('.jsx')) {
619
+ text = Compiler(text, file);
620
+
630
621
  }
631
-
632
- let baseFolder = componentFolder.split('node_modules')[1].split('/')[1]
633
- let glp = globSync('**/**/**/**.{jsx,js}', {
634
- cwd: process.cwd() + '/node_modules/' + baseFolder + '/',
635
- absolute: true,
636
- recursive: true
637
- })
638
- for (let file of glp) {
639
- let text = fs.readFileSync(file, "utf8");
640
- if(!file.endsWith('.js') && file.endsWith('.jsx')){
641
- text = Compiler(text, file);
642
- }
643
- let dest = file.split('node_modules')[1]
644
- dest = dest.split(baseFolder)[1]
645
- // write to dist
646
- writer(process.cwd() + '/dist/src/' + baseFolder + dest, text)
647
- let importname = match.split('import')[1].split('from')[0].trim()
648
- let oldImportstring = match.split('from')[1].trim().replace(/'/g, '').replace(/"/g, '').trim()
649
- let newImport = `/src/${baseFolder + dest}`
650
- newImport = newImport.replaceAll('.jsx', '.js').replaceAll('\\', '/')
651
- replaceMents.push({ match: oldImportstring, replace: newImport })
652
- console.log(`📦 imported Node Package ${baseFolder} `)
653
- }
654
-
622
+ let dest = file.split('node_modules')[1]
623
+ dest = dest.split(baseFolder)[1]
624
+ // write to dist
625
+ writer(process.cwd() + '/dist/src/' + baseFolder + dest, text)
626
+ let importname = match.split('import')[1].split('from')[0].trim()
627
+ let oldImportstring = match.split('from')[1].trim().replace(/'/g, '').replace(/"/g, '').trim()
628
+ let newImport = `/src/${baseFolder + dest}`
629
+ newImport = newImport.replaceAll('.jsx', '.js').replaceAll('\\', '/')
630
+ replaceMents.push({ match: oldImportstring, replace: newImport })
631
+ console.log(`📦 imported Node Package ${baseFolder} `)
632
+ }
633
+
655
634
 
656
635
  break;
657
636
  default:
658
637
  break;
659
638
  }
660
- }
661
-
662
- for(let replace of replaceMents){
639
+ }
640
+
641
+ for (let replace of replaceMents) {
663
642
  string = string.replaceAll(replace.match, replace.replace)
664
- }
643
+ }
665
644
 
666
645
  string = string.replaceAll(/\$\{[^{]*\.{3}/gs, (match) => {
667
646
  if (match.includes('...')) {
@@ -716,7 +695,7 @@ function Compiler(func, file) {
716
695
  let asyncimportMatch = line.match(/import\s*\((.*)\)/gs);
717
696
  // handle import { Component } from 'vaderjs/runtime/vader.js'
718
697
  let regularimportMatch = line.match(/import\s*([A-Za-z0-9_-]+)\s*from\s*([A-Za-z0-9_-]+)|import\s*([A-Za-z0-9_-]+)\s*from\s*(".*")|import\s*([A-Za-z0-9_-]+)\s*from\s*('.*')|import\s*([A-Za-z0-9_-]+)\s*from\s*(\{.*\})/gs);
719
-
698
+
720
699
  if (asyncimportMatch) {
721
700
  asyncimportMatch.forEach(async (match) => {
722
701
  let beforeimport = match
@@ -770,7 +749,7 @@ function Compiler(func, file) {
770
749
 
771
750
  let newImport = ''
772
751
  let name = match.split('import')[1].split('from')[0].trim()
773
-
752
+
774
753
 
775
754
  switch (true) {
776
755
  case path && path.includes('json'):
@@ -796,9 +775,9 @@ function Compiler(func, file) {
796
775
  newImport = ``
797
776
  break;
798
777
  case path && !path.startsWith('./') && !path.includes('/vader.js') && !path.startsWith('src') && !path.startsWith('public') &&
799
- path.match(/^[A-Za-z0-9_-]+$/gs) && !path.includes('http') && !path.includes('https'):
800
- console.log(path)
801
- break;
778
+ path.match(/^[A-Za-z0-9_-]+$/gs) && !path.includes('http') && !path.includes('https'):
779
+
780
+ break;
802
781
  default:
803
782
  let beforePath = path
804
783
  let deep = path.split('/').length - 1
@@ -856,14 +835,7 @@ async function Build() {
856
835
  return text;
857
836
  };
858
837
 
859
-
860
-
861
-
862
-
863
-
864
- // Process files in the 'pages' directory
865
- let appjs = '';
866
- let hasWritten = []
838
+
867
839
  function ssg(routes = []) {
868
840
  globalThis.isBuilding = true
869
841
  console.log(`Generating html files for ${routes.length} routes`)
@@ -1019,38 +991,47 @@ async function Build() {
1019
991
  headless: "new", args: ['--no-sandbox', '--disable-setuid-sandbox'],
1020
992
  warning: false,
1021
993
  })
1022
-
994
+
1023
995
  const browserPID = browser.process().pid
1024
996
  try {
1025
997
 
1026
998
  route.url = route.url.replaceAll(/\/:[a-zA-Z0-9_-]+/gs, '')
1027
999
  let page = await browser.newPage();
1028
1000
  await page.goto(`http://localhost:${port}/`, { waitUntil: 'networkidle2' });
1001
+ await page.on('console', msg => console.log('PAGE LOG:', msg.text()));
1002
+ await page.on('error', err => console.log('PAGE LOG:', err));
1003
+ await page.on('pageerror', err => console.log('PAGE LOG:', err));
1004
+ await page.evaluate(() => {
1005
+ window.onerror = function (msg, url, lineNo, columnNo, error) {
1006
+ console.log(msg, url, lineNo, columnNo, error)
1007
+ }
1008
+ })
1029
1009
  await page.waitForSelector('#root', { timeout: 10000 })
1030
1010
  await page.evaluate(() => {
1031
1011
  document.getElementById('meta').remove()
1032
1012
  document.querySelector('#isServer').innerHTML = 'window.isServer = false'
1033
1013
  if (document.head.getAttribute('prerender') === 'false') {
1034
1014
  document.querySelector('#root').innerHTML = ''
1015
+ console.log(`Disabled prerendering for ${window.location.pathname}`)
1035
1016
  }
1036
- })
1017
+ })
1037
1018
  const html = await page.content();
1038
1019
 
1039
1020
  await page.close();
1040
1021
  await writer(process.cwd() + '/dist/' + (route.url === '/' ? 'index.html' : `${route.url}/` + 'index.html'), html)
1041
1022
  await browser.close();
1042
1023
  server.close()
1043
-
1024
+
1044
1025
  } catch (error) {
1045
1026
  server.close()
1046
- await browser.close();
1027
+ await browser.close();
1047
1028
  }
1048
1029
  finally {
1049
1030
  await browser.close();
1050
- server.close()
1031
+ server.close()
1051
1032
  }
1052
1033
  try {
1053
- process.kill(browserPID )
1034
+ process.kill(browserPID)
1054
1035
  } catch (error) {
1055
1036
  }
1056
1037
 
@@ -1096,7 +1077,6 @@ async function Build() {
1096
1077
  data = Compiler(data, origin);
1097
1078
 
1098
1079
 
1099
-
1100
1080
  await writer(process.cwd() + "/dist/" + fileName.replace('.jsx', '.js'), data).then(async () => {
1101
1081
 
1102
1082
 
@@ -1259,7 +1239,7 @@ async function Build() {
1259
1239
  console.log(`📦 Build completed: Build Size -> ${Math.round(bundleSize / 1000)}kb`)
1260
1240
 
1261
1241
  bundleSize = 0;
1262
-
1242
+
1263
1243
  return true
1264
1244
  }
1265
1245
  const s = () => {
@@ -1389,7 +1369,7 @@ Vader.js v1.3.3
1389
1369
  }
1390
1370
  }).on('error', (err) => console.log(err))
1391
1371
  })
1392
- let p = process.argv[process.argv.indexOf('--watch') + 1] || process.env.PORT || 3000
1372
+ let p = process.argv[process.argv.indexOf('--watch') + 1] || process.env.PORT || 3000
1393
1373
 
1394
1374
  process.env.PORT = p
1395
1375
  s()