vaderjs 1.3.3-alpha-152 → 1.3.3-alpha-161

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-alpha-152",
5
+ "version": "1.3.3-alpha-161",
6
6
  "bin": {
7
7
  "vader": "./vader.js"
8
8
  },
@@ -27,8 +27,9 @@
27
27
  "email": "malikwhitterb@gmail.com"
28
28
  },
29
29
  "dependencies": {
30
- "glob": "latest",
31
- "terser":"latest",
32
- "puppeteer":"latest"
30
+ "glob": "latest",
31
+ "puppeteer":"latest",
32
+ "dotenv":"latest"
33
+
33
34
  }
34
35
  }
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
@@ -7,17 +7,23 @@ import { WebSocketServer } from 'ws'
7
7
  import { watch } from "fs";
8
8
  import path from 'path'
9
9
  let config = await import('file://' + process.cwd() + '/vader.config.js').then((e) => e.default || e)
10
+ let writer = async (file, data) => {
11
+ globalThis.isWriting = file
12
+ switch (true) {
13
+ case !fs.existsSync(file):
14
+ fs.mkdirSync(file.split('/').slice(0, -1).join('/'), { recursive: true })
15
+ break;
16
+ }
17
+ if (globalThis.isWriting !== file) {
18
+ return
19
+ }
20
+ await fs.writeFileSync(file, data);
10
21
 
11
-
12
- let start = Date.now()
13
- let bundleSize = 0;
14
- let errorCodes = {
15
- "SyntaxError: Unexpected token '<'": "You forgot to enclose tags in a fragment <></>",
16
- }
17
- /**
18
- * define directories
19
- */
20
-
22
+ globalThis.isWriting = null
23
+ return { _written: true };
24
+ };
25
+
26
+ let bundleSize = 0;
21
27
 
22
28
  if (!fs.existsSync(process.cwd() + '/dist')) {
23
29
  fs.mkdirSync(process.cwd() + '/dist')
@@ -34,44 +40,13 @@ if (typeof process.env.isCloudflare !== "undefined" || !fs.existsSync(process.cw
34
40
 
35
41
 
36
42
  function Compiler(func, file) {
37
- let string = func;
38
- // Remove block comments
39
-
43
+ let string = func;
40
44
  let returns = []
41
45
  let comments = string.match(/\{\s*\/\*.*\*\/\s*}/gs)?.map((comment) => comment.trim());
42
46
 
43
- let savedfuncnames = [];
44
- let functions = string.match(
45
- /(?:const|let)\s*([a-zA-Z0-9_-]+)\s*=\s*function\s*\(([^)]*)\)|function\s*([a-zA-Z0-9_-]+)\s*\(([^)]*)\)/gs
46
- )
47
- ?.map((match) => match.trim());
48
-
49
- let functionNames = [];
50
-
51
-
52
- functions && functions.forEach((func) => {
53
- if (
54
- !func.match(
55
- /(?:const|let)\s*([a-zA-Z0-9_-]+)\s*=\s*function\s*\(([^)]*)\)|function\s*([a-zA-Z0-9_-]+)\s*\(([^)]*)\)/gs
56
- )
57
- ) {
58
- return;
59
- }
60
-
61
- let name = func.split(" ")[1].split("(")[0].trim();
62
-
63
- let lines = string.match(/return\s*\<>.*\<\/>/gs);
64
-
65
- if (lines) {
66
- for (let i = 0; i < lines.length; i++) {
67
- let line = lines[i];
68
-
69
- if (!functionNames.includes(name)) {
70
- functionNames.push(name);
71
- }
72
- }
73
- }
74
- });
47
+
48
+
49
+
75
50
 
76
51
  // get all Obj({}) and parse to JSON.stringify
77
52
 
@@ -87,6 +62,51 @@ function Compiler(func, file) {
87
62
 
88
63
  let childs = [];
89
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
+
90
110
 
91
111
 
92
112
  function extractAttributes(code) {
@@ -98,41 +118,40 @@ function Compiler(func, file) {
98
118
  const attributeRegex =
99
119
  /\s*([a-zA-Z0-9_-]+)(\s*=\s*("([^"\\]*(\\.[^"\\]*)*)"|'([^'\\]*(\\.[^'\\]*)*)'|\{(?:[^{}]|(?:\{(?:[^{}]|(?:\{[^{}]*\})*)*\})*)*\}|(?:\([^)]*\)|\{[^}]*\}|()=>\s*(?:\{[^}]*\})?)|\[[^\]]*\]))?/gs;
100
120
 
101
- // only return elements with attribute {()=>{}} or if it also has parameters ex: onclick={(event)=>{console.log(event)}} also get muti line functions
121
+
122
+
123
+
102
124
  const functionAttributeRegex = /\s*([a-zA-Z0-9_-]+)(\s*=\s*{((?:[^{}]|(?:\{(?:[^{}]|(?:\{[^{}]*\})*)*\})*)*)})/gs;
103
125
 
104
126
  let attributesList = [];
105
127
 
106
- // handle functions
128
+ let spreadAttributes = [];
129
+ let spreadMatch;
130
+ /**
131
+ * @search - handle spread for html elements
132
+ * @keywords - spread, spread attributes, spread props, spread html attributes
133
+ */
134
+
135
+
136
+ /**
137
+ * @search - handle function parsing for html elements
138
+ * @keywords - function, function attributes, function props, function html attributes
139
+ *
140
+ */
107
141
  let functionAttributes = [];
108
142
  let functionMatch;
109
143
  while ((functionMatch = functionAttributeRegex.exec(code)) !== null) {
110
- let [, attributeName, attributeValue] = functionMatch;
111
144
 
145
+ let [, attributeName, attributeValue] = functionMatch;
112
146
  let attribute = {};
113
147
 
114
- if (attributeValue && attributeValue.includes("=>") || attributeValue && attributeValue.includes("function")) {
115
- let functionparams = [];
116
- // ref with no numbers
148
+ if (attributeValue && attributeValue.includes("=>") || attributeValue && attributeValue.includes("function")
149
+ && !spreadFunctions.includes(attributeValue)
150
+ ) {
151
+
117
152
  let ref = Math.random().toString(36).substring(2).split('').filter((e) => !Number(e)).join('')
118
153
  let old = `${attributeName}${attributeValue}`
119
- functionNames.forEach((name) => {
120
- string.split("\n").forEach((line) => {
121
- if (line.includes(name) && line.includes("function")) {
122
- line = line.trim();
123
- line = line.replace(/\s+/g, " ");
124
-
125
- let ps = line.split("(").slice(1).join("(").split(")")[0].trim();
126
-
127
- // remove comments
128
- ps = ps.match(/\/\*.*\*\//gs)
129
- ? ps.replace(ps.match(/\/\*.*\*\//gs)[0], "")
130
- : ps;
131
- functionparams.push({ ref: ref, name: name, params: ps });
132
-
133
- }
134
- });
135
- });
154
+
136
155
  let elementMatch = string.match(/<([a-zA-Z0-9_-]+)([^>]*)>/gs);
137
156
  let isJSXComponent = false;
138
157
  elementMatch.forEach((element) => {
@@ -186,8 +205,7 @@ function Compiler(func, file) {
186
205
  newvalue = newvalue.replace(/}\s*$/, '');
187
206
 
188
207
 
189
-
190
- functionparams.length > 0 ? params = params + ',' + functionparams.map((e) => e.name).join(',') : null
208
+
191
209
 
192
210
  newvalue = newvalue.replaceAll(',,', ',')
193
211
  let paramnames = params ? params.split(',').map((e) => e.trim()) : null
@@ -210,6 +228,10 @@ function Compiler(func, file) {
210
228
  }
211
229
  }
212
230
 
231
+ /**
232
+ * @search - handle attributes for html elements
233
+ * @keywords - attributes, props, html attributes
234
+ */
213
235
  let match;
214
236
  while ((match = elementRegex.exec(code)) !== null) {
215
237
  let [, element, attributes] = match;
@@ -234,8 +256,7 @@ function Compiler(func, file) {
234
256
  let returns = code.match(/return\s*\<>.*\<\/>|return\s*\(.*\)/gs);
235
257
 
236
258
  return returns || [];
237
- }
238
- // throw error if return is not wrapped in <></> or
259
+ }
239
260
  if (string.match(/return\s*\<>|return\s*\(.*\)/gs) && !string.match(/return\s*\<>.*\<\/>|return\s*\(.*\)/gs)
240
261
  || string.match(/return\s*\<[a-zA-Z0-9_-]+.*>/gs)
241
262
  ) {
@@ -251,6 +272,10 @@ function Compiler(func, file) {
251
272
  let outerReturn = extractOuterReturn(string);
252
273
  let contents = "";
253
274
  let updatedContents = "";
275
+ /**
276
+ * @search - handle return [...]
277
+ * @keywords - return, return jsx, return html, return [...]
278
+ */
254
279
  outerReturn.forEach((returnStatement) => {
255
280
 
256
281
  let lines = returnStatement.split("\n");
@@ -264,13 +289,12 @@ function Compiler(func, file) {
264
289
  }
265
290
  let usesBraces = returnStatement.match(/return\s*\(/gs) ? true : false;
266
291
 
267
- let attributes = extractAttributes(contents);
268
- // Remove trailing ']' or trailing )
292
+
269
293
  contents = contents.trim().replace(/\]$/, "")
270
294
  contents = contents.replace(/\)$/, "");
271
295
  usesBraces ? !contents.includes('<>') ? contents = `<>${contents}</>` : null : null
272
296
  updatedContents = contents;
273
-
297
+ let attributes = extractAttributes(contents);
274
298
 
275
299
  let newAttributes = [];
276
300
  let oldAttributes = [];
@@ -349,9 +373,7 @@ function Compiler(func, file) {
349
373
  valuestate = valuestate.match(regex) ? valuestate.match(regex)[0].split("useState(")[1].split(")")[0].trim() : valuestate
350
374
 
351
375
 
352
- let newState = `${varType} [${key}, ${setKey}] = this.useState('${key}', ${valuestate}
353
-
354
- `;
376
+ let newState = `${varType} [${key}, ${setKey}] = this.useState('${key}', ${valuestate}`;
355
377
  string = string.replace(line, newState);
356
378
  break;
357
379
  case line.includes("useRef") && !line.includes("import"):
@@ -396,25 +418,24 @@ function Compiler(func, file) {
396
418
  string = string.replaceAll('../src', './src')
397
419
 
398
420
  function parseComponents(body, isChild) {
399
- let componentRegex = /<([A-Z][A-Za-z0-9_-]+)([^>]*)>(.*?)<\/\1>|<([A-Z][A-Za-z0-9_-]+)([^]*?)\/>/gs;
421
+ let componentRegex = /<([A-Z][A-Za-z0-9_-]+)\s*([^>]*)>\s*([\s\S]*?)\s*<\/\1>|<([A-Z][A-Za-z0-9_-]+)([^]*?)\/>/gs;
400
422
 
401
423
  let componentMatch = body.match(componentRegex);
402
424
  let topComponent = "";
403
425
  componentMatch?.forEach(async (component) => {
404
426
 
405
427
  let [, element, attributes] = component;
406
-
428
+ let before = component;
429
+ component = component.trim().replace(/\s+/g, " ");
407
430
 
408
431
  !isChild ? (topComponent = component) : null;
409
- let before = component;
432
+
410
433
 
411
434
  let myChildrens = [];
412
435
 
413
436
  let name = component.split("<")[1].split(">")[0].split(" ")[0].replace("/", "");
414
- // 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'}}
415
- // grab ...( spread props )
416
- const dynamicAttributesRegex = /(\w+)(?:="([^"]*)")?(?:='([^']*)')?(?:=\{([^}]*)\})?(?:=\{(.*?)\})?(?:={([^}]*)})?(?:{([^}]*)})?|(?:{(?:[^{}]+|\{(?:[^{}]+|\{[^}]*\})*\})*\})|(\.{3}\{(?:[^{}]+|\{(?:[^{}]+|\{[^}]*\})*\})*\})/gs;
417
-
437
+ let componentAttributes = component.split("<")[1].split(">")[0].split(" ").join(" ").replace(name, "").trim();
438
+ const dynamicAttributesRegex = /(\w+)(?:="([^"]*)")?(?:='([^']*)')?(?:=\{([^}]*)\})?(?:=\{(.*?)\})?(?:={([^}]*)})?(?:{([^}]*)})?|(?:{(?:[^{}]+|\{(?:[^{}]+|\{[^}]*\})*\})*\})|(\.{3}\{(?:[^{}]+|\{(?:[^{}]+|\{[^}]*\})*\})*\})|\$=\{(?:[^{}]+|\{(?:[^{}]+|\{[^}]*\})*\})*\}/gs;
418
439
 
419
440
 
420
441
 
@@ -425,119 +446,21 @@ function Compiler(func, file) {
425
446
  let filteredProps = [];
426
447
  let isWithinComponent = false;
427
448
  let componentName = name
449
+ let currentProps = []
428
450
 
429
- for (let prop of props) {
430
-
431
- if (prop.includes(componentName)) {
432
- // If the component is encountered, start collecting props
433
- isWithinComponent = true;
434
- filteredProps.push(prop);
435
- } else if (isWithinComponent && prop.includes('=')) {
436
-
437
- if (prop.includes('${')) {
438
- // if it has an object inside of it then we should just do soemting:object else we should do something: `${object}`
439
-
440
- prop = prop.replace('="', ':').replace('}"', '}')
441
- if (prop.includes('${')) {
442
- prop = prop.replace('="', ':')
443
- prop = prop.replace('${', '')
444
- prop = prop.replace('}', '')
445
-
446
- }
447
- if (prop.includes('="${{')) {
448
- prop = prop.replace('${{', '{')
449
- prop = prop.replace('}}', '}')
450
- prop = prop.replace('="', ':')
451
- prop = prop.replace('}"', '}')
452
- }
453
-
454
- }
455
- if (prop.startsWith('={')) {
456
- prop = prop.replace('={', ':`${')
457
- prop.replace('} ', '}`')
458
- }
459
-
460
- if (prop.includes('function')) {
461
- // parse 'function' to function
462
- prop = prop.replace("'", '')
463
-
464
- if (prop.endsWith("}'")) {
465
- prop = prop.replace("}'", '}')
466
-
467
- }
468
-
469
- prop = prop.replace('=function', ':function')
470
- }
471
-
472
- filteredProps.push(prop);
473
-
451
+ let $_ternaryprops = []
452
+
453
+ component = component.replaceAll(/\s+/g, " ");
474
454
 
475
- }
476
- else if (isWithinComponent && prop.includes('...')) {
477
-
478
-
479
-
480
- // Check if spread props are within curly braces
481
- if (prop.startsWith('{') && prop.endsWith('}')) {
482
- const spreadObject = prop
483
- const hasOtherObjects = spreadObject.split(',').filter((e) => e.includes(':')).length > 0;
484
-
485
- const isValidSpread = spreadObject.includes('...');
486
-
487
- let processedSpreadObject = '';
488
- if (isValidSpread) {
489
- // Split the spreadObject by commas and process each part individually
490
- const parts = spreadObject.split(',').map((part) => {
491
- if (part.trim().startsWith('{') && part.trim().endsWith('}')) {
492
- const nestedParts = part
493
- .trim()
494
- .slice(1, -1) // Remove outer {}
495
- .split(',')
496
- .map((nestedPart) => {
497
- return nestedPart.includes('...') ? nestedPart.trim().replace(/\.\.\./, '') : `...${nestedPart.trim()}`;
498
- });
499
- return `{${nestedParts.join(',')}}`;
500
- } else {
501
- return part.includes('...') ? part.trim() : part.trim().startsWith('{') ? `...${part.trim()}` : `${part.trim()}`;
502
- }
503
- });
504
- if (!parts.join(',').includes('{(')) {
505
-
506
- processedSpreadObject = `${parts.join(',')}`
507
-
508
- } else {
509
- let prop = parts.join(',')
510
- prop = prop.replaceAll('{(', '(')
511
- prop = prop.replaceAll(')}', ')')
512
- processedSpreadObject = prop
513
- }
514
- if (prop.includes('{{')) {
515
- let prop = parts.join(',')
516
- prop = prop.replaceAll('{{', '{')
517
- prop = prop.replaceAll('}}', '}')
518
- processedSpreadObject = prop
519
- }
520
-
521
-
522
- } else {
523
- // Process nested structures within the object
524
- processedSpreadObject = `{...${spreadObject}}`;
525
- }
526
-
527
- const $propsKey = `$props_${Math.random().toString(36).substring(2)}`;
528
- filteredProps.push(`${$propsKey}:${processedSpreadObject}`);
529
- }
530
- }
455
+ component = component.replace(componentAttributes, '')
456
+ $_ternaryprops.forEach((prop) => {
457
+ component = component.replace(prop, '')
458
+ })
531
459
 
532
- else {
533
- isWithinComponent = false;
534
- }
535
- }
460
+ let children = component.split(`<${name}`)[1].split(`</${name}>`)[0].trim().replace(/\s+/g, " ").trim().replace(/,$/, '').replace('>', '').replace(/\/$/, '').trim()
536
461
 
537
- // get inner content of <Component>inner content</Component>
538
- let children = new RegExp(`<${name}[^>]*>(.*?)<\/${name}>`, "gs").exec(component) ? new RegExp(`<${name}[^>]*>(.*?)<\/${name}>`, "gs").exec(component)[1] : null;
539
462
 
540
- props = filteredProps.join(',')
463
+ props = filteredProps.join(',').replace(/\s+/g, " ").trim().replace(/,$/, '')
541
464
 
542
465
  let savedname = name;
543
466
 
@@ -555,16 +478,16 @@ function Compiler(func, file) {
555
478
  childs.forEach((child) => {
556
479
  if (child.parent == name) {
557
480
  let html = child.children.match(
558
- /<([a-zA-Z0-9_-]+)([^>]*)>(.*?)<\/\1>/gs
481
+ /<([A-Z][A-Za-z0-9_-]+)([^>]*)>(.*?)<\/\1>|<([A-Z][A-Za-z0-9_-]+)([^]*?)\/>/gs
559
482
  );
560
483
  if (html) {
561
484
  html = html.map((h) => h.trim().replace(/\s+/g, " ")).join(" ");
562
- let before = child.children;
563
485
  child.children = child.children.replaceAll(html, `${html}`);
564
486
  // remove duplicate quotes
565
487
  }
566
488
 
567
489
  myChildrens.push(child.children);
490
+ childs = childs.filter((e) => e.parent !== name);
568
491
  }
569
492
  });
570
493
 
@@ -585,8 +508,10 @@ function Compiler(func, file) {
585
508
  * @memoize - memoize a component to be remembered on each render and replace the old jsx
586
509
  */
587
510
 
511
+
588
512
  let replace = "";
589
- replace = `\${this.memoize(this.createComponent(${savedname}, {${props}}, [\`${myChildrens.join(" ")}\`]))}`;
513
+ replace = `\${this.memoize(this.createComponent(${savedname}, {${props}}, [\`${myChildrens.join('')}\`]))}`;
514
+
590
515
 
591
516
  body = body.replace(before, replace);
592
517
  });
@@ -594,12 +519,61 @@ function Compiler(func, file) {
594
519
  return body;
595
520
  }
596
521
 
522
+ string = string.replaceAll('vaderjs/client', '/vader.js')
597
523
 
524
+ const importRegex = /import\s*([^\s,]+|\{[^}]+\})\s*from\s*(['"])(.*?)\2/g;
525
+ const imports = string.match(importRegex);
526
+ let replaceMents = [];
598
527
 
599
- // replace all comments
600
528
 
601
- string = string.replaceAll('vaderjs/client', '/vader.js')
602
- // replace ${... with ${
529
+ for (let match of imports) {
530
+ let path = match.split('from')[1].trim().replace(/'/g, '').replace(/"/g, '').trim()
531
+ switch (true) {
532
+ case path && !path.includes('./') && !path.includes('/vader.js') && !path.includes('/vaderjs/client') && !path.startsWith('src') && !path.startsWith('public') && !path.includes('http') && !path.includes('https'):
533
+ let componentFolder = fs.existsSync(process.cwd() + '/node_modules/' + path) ? process.cwd() + '/node_modules/' + path : process.cwd() + '/node_modules/' + path.split('/')[0]
534
+ componentFolder = componentFolder.split(process.cwd())[1]
535
+ if (!fs.existsSync(process.cwd() + componentFolder)) {
536
+ throw new Error('Could not find ' + path + ' at ' + match + ' in file ' + file)
537
+ }
538
+
539
+ if (!fs.existsSync(process.cwd() + '/dist/src/' + componentFolder.split('/').slice(2).join('/'))) {
540
+ fs.mkdirSync(process.cwd() + '/dist/src/' + componentFolder.split('/').slice(2).join('/'), { recursive: true })
541
+ }
542
+
543
+ let baseFolder = componentFolder.split('node_modules')[1].split('/')[1]
544
+ let glp = globSync('**/**/**/**.{jsx,js}', {
545
+ cwd: process.cwd() + '/node_modules/' + baseFolder + '/',
546
+ absolute: true,
547
+ recursive: true
548
+ })
549
+ for (let file of glp) {
550
+ let text = fs.readFileSync(file, "utf8");
551
+ if (!file.endsWith('.js') && file.endsWith('.jsx')) {
552
+ text = Compiler(text, file);
553
+
554
+ }
555
+ let dest = file.split('node_modules')[1]
556
+ dest = dest.split(baseFolder)[1]
557
+ // write to dist
558
+ writer(process.cwd() + '/dist/src/' + baseFolder + dest, text)
559
+ let importname = match.split('import')[1].split('from')[0].trim()
560
+ let oldImportstring = match.split('from')[1].trim().replace(/'/g, '').replace(/"/g, '').trim()
561
+ let newImport = `/src/${baseFolder + dest}`
562
+ newImport = newImport.replaceAll('.jsx', '.js').replaceAll('\\', '/')
563
+ replaceMents.push({ match: oldImportstring, replace: newImport })
564
+ console.log(`📦 imported Node Package ${baseFolder} `)
565
+ }
566
+
567
+
568
+ break;
569
+ default:
570
+ break;
571
+ }
572
+ }
573
+
574
+ for (let replace of replaceMents) {
575
+ string = string.replaceAll(replace.match, replace.replace)
576
+ }
603
577
 
604
578
  string = string.replaceAll(/\$\{[^{]*\.{3}/gs, (match) => {
605
579
  if (match.includes('...')) {
@@ -652,7 +626,8 @@ function Compiler(func, file) {
652
626
  if (line.includes('import')) {
653
627
  // Regular expression for matching import() statements
654
628
  let asyncimportMatch = line.match(/import\s*\((.*)\)/gs);
655
- let regularimportMatch = line.match(/import\s*([a-zA-Z0-9_-]+)?\s*from\s*('(.*)'|"(.*)")|import\s*('(.*)'|"(.*)")/gs);
629
+ // handle import { Component } from 'vaderjs/runtime/vader.js'
630
+ 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);
656
631
 
657
632
  if (asyncimportMatch) {
658
633
  asyncimportMatch.forEach(async (match) => {
@@ -731,6 +706,10 @@ function Compiler(func, file) {
731
706
  case path && path.includes('.css'):
732
707
  string = string.replace(beforeimport, '')
733
708
  newImport = ``
709
+ break;
710
+ case path && !path.startsWith('./') && !path.includes('/vader.js') && !path.startsWith('src') && !path.startsWith('public') &&
711
+ path.match(/^[A-Za-z0-9_-]+$/gs) && !path.includes('http') && !path.includes('https'):
712
+
734
713
  break;
735
714
  default:
736
715
  let beforePath = path
@@ -789,28 +768,7 @@ async function Build() {
789
768
  return text;
790
769
  };
791
770
 
792
- let writer = async (file, data) => {
793
- globalThis.isWriting = file
794
- switch (true) {
795
- case !fs.existsSync(file):
796
- fs.mkdirSync(file.split('/').slice(0, -1).join('/'), { recursive: true })
797
- break;
798
- }
799
- if (globalThis.isWriting !== file) {
800
- return
801
- }
802
- await fs.writeFileSync(file, data);
803
-
804
- globalThis.isWriting = null
805
- return { _written: true };
806
- };
807
-
808
-
809
-
810
-
811
- // Process files in the 'pages' directory
812
- let appjs = '';
813
- let hasWritten = []
771
+
814
772
  function ssg(routes = []) {
815
773
  globalThis.isBuilding = true
816
774
  console.log(`Generating html files for ${routes.length} routes`)
@@ -966,33 +924,49 @@ async function Build() {
966
924
  headless: "new", args: ['--no-sandbox', '--disable-setuid-sandbox'],
967
925
  warning: false,
968
926
  })
969
-
927
+
928
+ const browserPID = browser.process().pid
970
929
  try {
971
930
 
972
931
  route.url = route.url.replaceAll(/\/:[a-zA-Z0-9_-]+/gs, '')
973
932
  let page = await browser.newPage();
974
933
  await page.goto(`http://localhost:${port}/`, { waitUntil: 'networkidle2' });
975
- await page.waitForSelector('#root');
934
+ await page.on('console', msg => console.log('PAGE LOG:', msg.text()));
935
+ await page.on('error', err => console.log('PAGE LOG:', err));
936
+ await page.on('pageerror', err => console.log('PAGE LOG:', err));
937
+ await page.evaluate(() => {
938
+ window.onerror = function (msg, url, lineNo, columnNo, error) {
939
+ console.log(msg, url, lineNo, columnNo, error)
940
+ }
941
+ })
942
+ await page.waitForSelector('#root', { timeout: 10000 })
976
943
  await page.evaluate(() => {
977
944
  document.getElementById('meta').remove()
978
945
  document.querySelector('#isServer').innerHTML = 'window.isServer = false'
979
946
  if (document.head.getAttribute('prerender') === 'false') {
980
947
  document.querySelector('#root').innerHTML = ''
948
+ console.log(`Disabled prerendering for ${window.location.pathname}`)
981
949
  }
982
- })
950
+ })
983
951
  const html = await page.content();
984
952
 
985
953
  await page.close();
986
954
  await writer(process.cwd() + '/dist/' + (route.url === '/' ? 'index.html' : `${route.url}/` + 'index.html'), html)
987
955
  await browser.close();
956
+ server.close()
957
+
988
958
  } catch (error) {
989
- console.log(error)
959
+ server.close()
990
960
  await browser.close();
991
961
  }
992
962
  finally {
993
963
  await browser.close();
994
964
  server.close()
995
965
  }
966
+ try {
967
+ process.kill(browserPID)
968
+ } catch (error) {
969
+ }
996
970
 
997
971
 
998
972
  })
@@ -1033,10 +1007,10 @@ async function Build() {
1033
1007
 
1034
1008
 
1035
1009
  let data = await fs.readFileSync(origin, "utf8");
1010
+ console.log(`Compiling ${fileName}...`)
1036
1011
  data = Compiler(data, origin);
1037
1012
 
1038
1013
 
1039
-
1040
1014
  await writer(process.cwd() + "/dist/" + fileName.replace('.jsx', '.js'), data).then(async () => {
1041
1015
 
1042
1016
 
@@ -1199,6 +1173,7 @@ async function Build() {
1199
1173
  console.log(`📦 Build completed: Build Size -> ${Math.round(bundleSize / 1000)}kb`)
1200
1174
 
1201
1175
  bundleSize = 0;
1176
+
1202
1177
  return true
1203
1178
  }
1204
1179
  const s = () => {
@@ -1328,7 +1303,9 @@ Vader.js v1.3.3
1328
1303
  }
1329
1304
  }).on('error', (err) => console.log(err))
1330
1305
  })
1306
+ let p = process.argv[process.argv.indexOf('--watch') + 1] || process.env.PORT || 3000
1331
1307
 
1308
+ process.env.PORT = p
1332
1309
  s()
1333
1310
 
1334
1311
  globalThis.listen = true;
@@ -1357,19 +1334,19 @@ url: http://localhost:${port}
1357
1334
  default:
1358
1335
  console.log(`
1359
1336
  Vader.js is a reactive framework for building interactive applications for the web built ontop of bun.js!
1360
-
1337
+
1361
1338
  Usage: vader <command>
1362
-
1339
+
1363
1340
  Commands:
1364
- --watch Watch the pages folder for changes with hot reloading
1365
-
1341
+ --watch (port) Watch the pages folder for changes with hot reloading
1342
+
1366
1343
  --build Build the project to ./dist
1367
-
1368
- --serve Serve the project on a port (default 3000)
1369
-
1370
- Learn more about vader: https://vader-js.pages.dev/
1371
1344
 
1372
- `)
1345
+ --serve (400) Serve the project on a port (default 3000 or process.env.PORT)
1346
+
1347
+ Learn more about vader: https://vader-js.pages.dev/
1348
+
1349
+ `)
1373
1350
  break;
1374
1351
 
1375
1352
  }