vaderjs 1.3.3-alpha-150 → 1.3.3-alpha-152

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
@@ -66,56 +66,27 @@ vader's compiler automatically handles routing so you wont need to! - it uses a
66
66
  For pages that have [params] you can derive it using this.request
67
67
 
68
68
 
69
- ### Simplified Component Creation
70
-
71
- Class based components
69
+ # Usage
72
70
 
71
+
73
72
  ```jsx
74
73
  // pages/home.jsx
75
74
  import {Component, useState, useRef} = from 'vaderjs/client'
76
75
  import Mycomponent from './src/mycomponent.jsx'
77
76
 
78
- export default class extends Component {
79
- constructor() {
80
- super();
81
- this.key = 2 // you can explicitly set key
82
- }
83
- render() {
84
- return <>
85
- ${/**
86
- or set it directly to the element and hydration will grab it
87
- **/}
88
- <div key={2}>
89
- <p>Hello World</p>
90
- </div>
91
- <Mycomponent ..props />
92
- </>
93
- }
94
- }
95
-
96
77
 
97
-
98
- ```
99
-
100
- Function based components
101
-
102
- ```jsx
103
- import Mycomponent from './src/mycomponent.jsx'
104
- // function components have direct access to request and response both param way and using this.request or this.response!
105
78
  export default function(req, res){
106
- this.key = '' // you can explicitly set key
107
-
108
- return <>
109
- ${/**
110
- or set it directly to the element and hydration will grab it
111
- **/}
112
- <div key="somevalue">
113
- <h1>hello world</>
114
- <Mycomponent {...props }/>
115
- </div>
116
- </>
79
+ let counterRef = useRef(null)
80
+ let [count, setCount] = useState(0)
81
+
82
+ return <>
83
+ <h1>${count}</h1>
84
+ <button onClick={(count, setCount)=>{setCount(count + 1)}}>
85
+ </>
117
86
  }
87
+
118
88
 
89
+
119
90
  ```
120
91
 
121
92
  # ServerSide Site Generation (SSG)
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-150",
5
+ "version": "1.3.3-alpha-152",
6
6
  "bin": {
7
7
  "vader": "./vader.js"
8
8
  },
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=[];Object.keys(t).forEach((e=>{e.startsWith("$props")&&(i.push(t[e]),delete t[e])})),t=t?{...t,...i.reduce(((e,t)=>({...e,...t})),{}),children:s.join("")||[]}:{children:s.join("")||[]};let o=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(),o=n;else{e.toString();o.key=e.toString().split('key="')[1]?e.toString().split('key="')[1].split('"')[0]:null;let r={key:o.key?o.key:Math.random(),isUnique:!!o.key,render:()=>e.apply(o,[t]),request:this.request,isChild:!0,response:this.response,params:this.request.params,queryParams:this.request.query,reset:o.reset.bind(o),onMount:o.onMount.bind(o),useState:null,router:{use:o.router.use.bind(o)},bindMount:o.bindMount.bind(o),memoize:o.memoize.bind(o),createComponent:o.createComponent.bind(o),isChild:!1,useState:o.useState.bind(o),parseStyle:o.parseStyle.bind(o),bind:o.bind.bind(o),useRef:o.useRef.bind(o),request:this.request,response:this.response,useReducer:o.useReducer.bind(o),hydrate:o.hydrate.bind(o),onUnmount:o.onUnmount.bind(o),parentNoe:this,props:{...t,children:s.join("")||[]}};o.render=r.render,o=r,o.props.children=s.join("")||[]}return this.components[o.key]||(this.components[o.key]=o),!this.children.includes(o)&&this.children.push(o),this.components[o.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){(new DOMParser).parseFromString(this.render(),"text/html").body.querySelector(`[ref="${e}"]`),document.querySelector(`[ref="${e}"]`)}else{let e=this.key?document.querySelector(`[key="${this.key}"]`):null,t=(new DOMParser).parseFromString(this.render(),"text/html").body;if(t=document.createElement("div").appendChild(t),!e&&(e=document.querySelector(`[key="${t.attributes?.key?.value||null}"]`)),!e)return void console.error('Hydration failed, component not found got ensure you have set key="a value" on the component or this.key inside of function or render method body');t.querySelectorAll("*").forEach((e=>{e.hasAttribute("key")&&e.innerHTML!==document.querySelector(`[key="${e.attributes.key.value}"]`).innerHTML&&document.querySelector(`[key="${e.attributes.key.value}"]`).replaceWith(e)}))}}patch(e,t){const 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){s=s+this.key||2022;let i={},o=(r=r.replace(/,,/g,",")).replaceAll(",,",",");for(var h in n){let e=n[h];i[o.split(",")[h]]=e}r=r.replace(",,",",");let l=null;e=e.split("\n").join(";");try{l=new Function(`event, ${r}`,` \n return (async (event, ${r}) => { \n ${e.toString()}\n })(event, ${Object.keys(i).join(",")}) \n `)}catch(e){let{message:t}=e;console.error(`Error in function ${s} ${t}`)}return l=l.bind(this),this.functions.find((e=>e.ref===s))||document.addEventListener(`$dispatch_#id=${s}`,(e=>{let{name:t,event:r}=e.detail;if(t===s){let e=this.functions.find((e=>e.ref===s)).params;Object.keys(e).forEach((t=>{e[t]instanceof CustomEvent&&delete e[t],void 0===e[t]?delete e[t]:e[t]})),l(r,...Object.values(e))}})),window.callFunction=(e,t)=>{document.dispatchEvent(new CustomEvent(`$dispatch_#id=${e}`,{detail:{name:e,params:null,event:t}}))},!this.functions.find((e=>e.ref===s))&&this.functions.push({ref:s,params:i}),t?e:`((event)=>{event.target.ev = event; callFunction('${s}', event.target.ev)})(event)`}useState(e,t){this.state[e]||(this.state[e]=t);let s=()=>this.state[e],r=s();return[r,(t,n)=>{this.state[e]=t,this.hydrate(n),r=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)=>{states[e]||(states[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 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};
package/vader.js CHANGED
@@ -4,10 +4,10 @@ import { glob, globSync, globStream, globStreamSync, Glob, } from 'glob'
4
4
  import puppeteer from 'puppeteer';
5
5
  import http from 'http'
6
6
  import { WebSocketServer } from 'ws'
7
- import { watch } from "fs";
7
+ import { watch } from "fs";
8
8
  import path from 'path'
9
- let config = await import('file://' + process.cwd() + '/vader.config.js').then((e) => e.default || e)
10
-
9
+ let config = await import('file://' + process.cwd() + '/vader.config.js').then((e) => e.default || e)
10
+
11
11
 
12
12
  let start = Date.now()
13
13
  let bundleSize = 0;
@@ -31,7 +31,7 @@ if (typeof process.env.isCloudflare !== "undefined" || !fs.existsSync(process.cw
31
31
  fs.writeFileSync(process.cwd() + "/dist/index.html", htmlFile)
32
32
  }
33
33
 
34
-
34
+
35
35
 
36
36
  function Compiler(func, file) {
37
37
  let string = func;
@@ -108,6 +108,7 @@ function Compiler(func, file) {
108
108
  let functionMatch;
109
109
  while ((functionMatch = functionAttributeRegex.exec(code)) !== null) {
110
110
  let [, attributeName, attributeValue] = functionMatch;
111
+
111
112
  let attribute = {};
112
113
 
113
114
  if (attributeValue && attributeValue.includes("=>") || attributeValue && attributeValue.includes("function")) {
@@ -143,7 +144,7 @@ function Compiler(func, file) {
143
144
  .split(" ")[0];
144
145
  isJSXComponent = elementTag.match(/^[A-Z]/) ? true : false;
145
146
  }
146
- });
147
+ });
147
148
  // add ; after newlines
148
149
 
149
150
 
@@ -193,16 +194,18 @@ function Compiler(func, file) {
193
194
  paramnames = paramnames ? paramnames.filter((e) => e.length > 0) : null
194
195
  // remove comments
195
196
  paramnames = paramnames ? paramnames.map((e) => e.match(/\/\*.*\*\//gs) ? e.replace(e.match(/\/\*.*\*\//gs)[0], "") : e) : null
196
- newvalue = newvalue.replaceAll(/\s+/g, " ")
197
-
198
- let bind = isJSXComponent ? `${attributeName}='function(${params}){${newvalue}}'` : `${attributeName}="\$\{this.bind("${newvalue.replace(/\s+g/, " ")}", ${isJSXComponent}, "${ref}", "${paramnames ? paramnames.map((e, index) => {
197
+
198
+ // add ; after newlines
199
+ newvalue = newvalue.replaceAll(/\n/g, ";\n")
200
+
201
+ let bind = isJSXComponent ? `${attributeName}='function(${params}){${newvalue}}'` : `${attributeName}="\$\{this.bind(function(){${newvalue}}.bind(this), ${isJSXComponent}, "${ref}", "${paramnames ? paramnames.map((e, index) => {
199
202
  if (e.length < 1) return ''
200
203
  if (e.length > 0) {
201
204
  index == 0 ? e : ',' + e
202
205
  }
203
206
  return e
204
207
  }) : ''}" ${params ? params.split(',').map((e) => e.trim()).filter(Boolean).map((e) => `,${e}`).join('') : ''})}"`
205
-
208
+
206
209
  string = string.replace(old, bind);
207
210
  }
208
211
  }
@@ -228,15 +231,21 @@ function Compiler(func, file) {
228
231
 
229
232
  function extractOuterReturn(code) {
230
233
  // match return [...]
231
- let returns = code.match(/return\s*\<>.*\<\/>/gs)
234
+ let returns = code.match(/return\s*\<>.*\<\/>|return\s*\(.*\)/gs);
232
235
 
233
236
  return returns || [];
234
237
  }
235
- // throw error if return is not wrapped in <></> if return is found and not wrapped in <></> or <div></div> and not <><div></div></>
236
- if (string.match(/return\s*\<>/gs) && !string.match(/return\s*\<>.*\<\/>/gs)
238
+ // throw error if return is not wrapped in <></> or
239
+ if (string.match(/return\s*\<>|return\s*\(.*\)/gs) && !string.match(/return\s*\<>.*\<\/>|return\s*\(.*\)/gs)
237
240
  || string.match(/return\s*\<[a-zA-Z0-9_-]+.*>/gs)
238
241
  ) {
239
- throw new SyntaxError("You forgot to enclose jsx in a fragment <></> at line " + string.split(/return\s*\<[a-zA-Z0-9_-]+.*>/gs)[0].split('\n').length + ' in file ' + file)
242
+
243
+ try {
244
+ throw new SyntaxError("You forgot to enclose jsx in a fragment return <> jsx html </> or return (<> jsx html </>) at line " + string.split(/return\s*\<[a-zA-Z0-9_-]+.*>/gs)[0].split('\n').length + ' in file ' + file)
245
+ } catch (error) {
246
+ console.error(error)
247
+ process.exit(1)
248
+ }
240
249
  }
241
250
 
242
251
  let outerReturn = extractOuterReturn(string);
@@ -248,16 +257,20 @@ function Compiler(func, file) {
248
257
 
249
258
  for (let i = 0; i < lines.length; i++) {
250
259
  let line = lines[i];
251
- if (line.match(/return\s*\<>/gs)) {
260
+ if (line.match(/return\s*\<>|return\s*\(/gs)) {
252
261
  continue;
253
262
  }
254
263
  contents += line + "\n";
255
264
  }
265
+ let usesBraces = returnStatement.match(/return\s*\(/gs) ? true : false;
256
266
 
257
- // Remove trailing ']'
258
- contents = contents.trim().replace(/\]$/, "");
259
- updatedContents = contents;
260
267
  let attributes = extractAttributes(contents);
268
+ // Remove trailing ']' or trailing )
269
+ contents = contents.trim().replace(/\]$/, "")
270
+ contents = contents.replace(/\)$/, "");
271
+ usesBraces ? !contents.includes('<>') ? contents = `<>${contents}</>` : null : null
272
+ updatedContents = contents;
273
+
261
274
 
262
275
  let newAttributes = [];
263
276
  let oldAttributes = [];
@@ -281,6 +294,7 @@ function Compiler(func, file) {
281
294
  ? value = `{this.parseStyle({${value.split('{{')[1].split('}}')[0]}})}` : null
282
295
 
283
296
 
297
+
284
298
  value = `="\$${value}",`;
285
299
  string = string.replace(oldvalue, value);
286
300
 
@@ -292,34 +306,13 @@ function Compiler(func, file) {
292
306
 
293
307
  }
294
308
  } else if (value && value.new) {
295
- string = string.replace(value.old, value.new);
309
+ string = string.replace(oldvalue, value.new);
296
310
  }
297
311
  }
298
312
  });
299
313
  });
300
314
 
301
- let retursnString = [];
302
- let outerReturnString = extractOuterReturn(string);
303
315
 
304
- outerReturnString.forEach((returnStatement) => {
305
- let lines = returnStatement.split("\n");
306
- let code = "";
307
- for (let i = 0; i < lines.length; i++) {
308
- let line = lines[i];
309
-
310
- code += line + "\n";
311
- }
312
-
313
- code = code.trim().replace(/\<\/\>$/, "");
314
- retursnString.push(code);
315
- });
316
-
317
- retursnString.forEach((returnStatement, index) => {
318
- let old = outerReturnString[index];
319
-
320
- let newReturn = `${returnStatement}</>`;
321
- string = string.replace(old, newReturn);
322
- });
323
316
 
324
317
  if (comments) {
325
318
  comments.forEach((comment) => {
@@ -408,149 +401,154 @@ function Compiler(func, file) {
408
401
  let componentMatch = body.match(componentRegex);
409
402
  let topComponent = "";
410
403
  componentMatch?.forEach(async (component) => {
411
-
404
+
412
405
  let [, element, attributes] = component;
413
-
406
+
414
407
 
415
408
  !isChild ? (topComponent = component) : null;
416
- let before = component;
409
+ let before = component;
417
410
 
418
411
  let myChildrens = [];
419
412
 
420
413
  let name = component.split("<")[1].split(">")[0].split(" ")[0].replace("/", "");
421
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'}}
422
415
  // grab ...( spread props )
423
- const dynamicAttributesRegex = /(\w+)(?:="([^"]*)")?(?:='([^']*)')?(?:=\{([^}]*)\})?(?:=\{(.*?)\})?(?:={([^}]*)})?(?:{([^}]*)})?|(\{(?:[^{}]+|\{(?:[^{}]+|\{[^}]*\})*\})*\})/gs;
416
+ const dynamicAttributesRegex = /(\w+)(?:="([^"]*)")?(?:='([^']*)')?(?:=\{([^}]*)\})?(?:=\{(.*?)\})?(?:={([^}]*)})?(?:{([^}]*)})?|(?:{(?:[^{}]+|\{(?:[^{}]+|\{[^}]*\})*\})*\})|(\.{3}\{(?:[^{}]+|\{(?:[^{}]+|\{[^}]*\})*\})*\})/gs;
417
+
418
+
424
419
 
425
420
 
426
421
 
427
422
 
428
-
429
423
  let props = component.match(dynamicAttributesRegex)
430
-
431
- let filteredProps = [];
432
- let isWithinComponent = false;
433
- let componentName = name
434
-
435
- for (let prop of props) {
436
-
437
- if (prop.includes(componentName)) {
438
- // If the component is encountered, start collecting props
439
- isWithinComponent = true;
440
- filteredProps.push(prop);
441
- } else if (isWithinComponent && prop.includes('=')) {
442
-
443
- if(prop.includes('${')){
444
- // if it has an object inside of it then we should just do soemting:object else we should do something: `${object}`
445
-
446
- prop = prop.replace('="', ':').replace('}"', '}')
447
- if(prop.includes('${')){
448
- prop = prop.replace('="',':')
449
- prop = prop.replace('${', '')
450
- prop = prop.replace('}', '')
451
-
452
- }
453
- if(prop.includes('="${{')){
454
- prop = prop.replace('${{', '{')
455
- prop = prop.replace('}}', '}')
456
- prop = prop.replace('="', ':')
457
- prop = prop.replace('}"', '}')
458
- }
459
-
460
- }
461
- if(prop.startsWith('={')){
462
- prop = prop.replace('={', ':`${')
463
- prop.replace('} ', '}`')
464
- }
465
-
466
- if(prop.includes('function')){
467
- // parse 'function' to function
468
- prop = prop.replace("'", '')
469
-
470
- if(prop.endsWith("}'")){
471
- prop = prop.replace("}'", '}')
472
- console
473
-
424
+
425
+ let filteredProps = [];
426
+ let isWithinComponent = false;
427
+ let componentName = name
428
+
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
+
474
+
474
475
  }
475
-
476
- prop = prop.replace('=function', ':function')
477
- }
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('{(')) {
478
505
 
479
- filteredProps.push(prop);
480
-
481
-
482
- }
483
- else if (isWithinComponent && prop.includes('...')) {
484
-
485
-
486
-
487
- // Check if spread props are within curly braces
488
- if (prop.startsWith('{') && prop.endsWith('}')) {
489
- const spreadObject = prop
490
- const hasOtherObjects = spreadObject.split(',').filter((e) => e.includes(':')).length > 0;
491
-
492
- const isValidSpread = spreadObject.includes('...');
493
-
494
- let processedSpreadObject = '';
495
- if (isValidSpread) {
496
- // Split the spreadObject by commas and process each part individually
497
- const parts = spreadObject.split(',').map((part) => {
498
- if (part.trim().startsWith('{') && part.trim().endsWith('}')) {
499
- // Handle nested spreads within the object
500
- const nestedParts = part
501
- .trim()
502
- .slice(1, -1) // Remove outer {}
503
- .split(',')
504
- .map((nestedPart) => {
505
- return nestedPart.includes('...') ? nestedPart.trim().replace(/\.\.\./, '') : `...${nestedPart.trim()}`;
506
- });
507
- return `{${nestedParts.join(',')}}`;
508
- } else {
509
- return part.includes('...') ? part.trim() : part.trim().startsWith('{') ? `...${part.trim()}` : `${part.trim()}`;
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}}`;
510
525
  }
511
- });
512
- if(!parts.join(',').includes('{(')){
513
-
514
- processedSpreadObject = `${parts.join(',')}`
515
-
516
- }else{
517
- let prop = parts.join(',')
518
- prop = prop.replaceAll('{(', '(')
519
- prop = prop.replaceAll(')}', ')')
520
- processedSpreadObject = prop
526
+
527
+ const $propsKey = `$props_${Math.random().toString(36).substring(2)}`;
528
+ filteredProps.push(`${$propsKey}:${processedSpreadObject}`);
521
529
  }
522
-
523
-
524
- } else {
525
- // Process nested structures within the object
526
- processedSpreadObject = `{...${spreadObject}}`;
527
- }
528
-
529
- const $propsKey = `$props_${Math.random().toString(36).substring(2)}`;
530
- filteredProps.push(`${$propsKey}:${processedSpreadObject}`);
531
- }
532
- }
533
-
534
- else{
535
- isWithinComponent = false;
536
- }
537
- }
530
+ }
531
+
532
+ else {
533
+ isWithinComponent = false;
534
+ }
535
+ }
538
536
 
539
537
  // get inner content of <Component>inner content</Component>
540
538
  let children = new RegExp(`<${name}[^>]*>(.*?)<\/${name}>`, "gs").exec(component) ? new RegExp(`<${name}[^>]*>(.*?)<\/${name}>`, "gs").exec(component)[1] : null;
541
-
539
+
542
540
  props = filteredProps.join(',')
543
-
541
+
544
542
  let savedname = name;
545
-
546
-
543
+
544
+
547
545
 
548
546
  name = name + Math.random().toString(36).substring(2);
549
547
  if (children && children.match(componentRegex)) {
550
548
  children = parseComponents(children, true);
551
549
  childs.push({ parent: name, children: children });
552
550
  } else {
553
-
551
+
554
552
  children ? childs.push({ parent: name, children: children }) : null;
555
553
  }
556
554
 
@@ -572,24 +570,22 @@ function Compiler(func, file) {
572
570
 
573
571
 
574
572
 
575
-
576
- props = props.replaceAll(`,${savedname}`, '').replaceAll(savedname, '')
577
- if(props.startsWith(',')){
578
- props = props.replace(',', '')
579
- }
580
- props = props .replaceAll("='", ":'")
581
- .replaceAll('=`', ':`')
573
+
574
+ props = props.replaceAll(`,${savedname}`, '').replaceAll(savedname, '')
575
+ if (props.startsWith(',')) {
576
+ props = props.replace(',', '')
577
+ }
578
+ props = props.replaceAll("='", ":'")
579
+ .replaceAll('=`', ':`')
582
580
  .replaceAll('="', ':"')
583
- .replaceAll(`={\``, ':`')
584
- .replaceAll('`}', '`')
585
- .replaceAll('={', ":`")
586
-
581
+ .replaceAll('={', ':')
582
+
587
583
 
588
584
  /**
589
585
  * @memoize - memoize a component to be remembered on each render and replace the old jsx
590
586
  */
591
587
 
592
- let replace = "";
588
+ let replace = "";
593
589
  replace = `\${this.memoize(this.createComponent(${savedname}, {${props}}, [\`${myChildrens.join(" ")}\`]))}`;
594
590
 
595
591
  body = body.replace(before, replace);
@@ -779,6 +775,12 @@ function Compiler(func, file) {
779
775
 
780
776
  globalThis.isBuilding = false
781
777
  globalThis.isWriting = null
778
+ const glb = await glob("**/**/**/**.{jsx,js}", {
779
+ ignore: ["node_modules/**/*", "dist/**/*"],
780
+ cwd: process.cwd() + '/pages/',
781
+ absolute: true,
782
+ recursive: true
783
+ });
782
784
  async function Build() {
783
785
  globalThis.isBuilding = true
784
786
  console.log('Compiling......')
@@ -804,12 +806,7 @@ async function Build() {
804
806
  };
805
807
 
806
808
 
807
- const glb = await glob("**/**/**/**.{jsx,js}", {
808
- ignore: ["node_modules/**/*", "dist/**/*"],
809
- cwd: process.cwd() + '/pages/',
810
- absolute: true,
811
- recursive: true
812
- });
809
+
813
810
 
814
811
  // Process files in the 'pages' directory
815
812
  let appjs = '';
@@ -821,10 +818,10 @@ async function Build() {
821
818
  if (route.url.includes(':')) {
822
819
  console.log('Route ' + route.url + ' is a dynamic route and will not be generated')
823
820
  return
824
- }
825
- let equalparamroute = routes.map((e) => {
821
+ }
822
+ let equalparamroute = routes.map((e) => {
826
823
  if (e.url.includes(':')) {
827
- let url = e.url.split('/:')[0]
824
+ let url = e.url.split('/:')[0]
828
825
  if (url && route.url === url) {
829
826
  return e
830
827
  } else {
@@ -908,14 +905,14 @@ async function Build() {
908
905
  res.render(module, req, res, module.$metadata)
909
906
  })
910
907
  ${equalparamroute.length > 0 ? equalparamroute.map((e) => {
911
-
912
-
913
-
914
- return `router.get('${e.url}', async (req, res) => {
908
+
909
+
910
+
911
+ return `router.get('${e.url}', async (req, res) => {
915
912
  let module = await import('/${e.fileName.replace('.jsx', '.js')}')
916
913
  res.render(module, req, res, module.$metadata)
917
914
  })\n`
918
- }) : ''}
915
+ }) : ''}
919
916
  router.listen(3000)
920
917
 
921
918
  </script>
@@ -932,8 +929,8 @@ async function Build() {
932
929
  let port = Math.floor(Math.random() * (65535 - 49152 + 1) + 49152)
933
930
 
934
931
  const server = http.createServer((req, res) => {
935
-
936
- if (req.url === '/') {
932
+
933
+ if (req.url === '/') {
937
934
  res.writeHead(200, { 'Content-Type': 'text/html' });
938
935
  res.end(document);
939
936
  } else {
@@ -965,26 +962,26 @@ async function Build() {
965
962
 
966
963
  globalThis.listen = true;
967
964
 
968
- const browser = await puppeteer.launch({
969
- headless: "new", args: ['--no-sandbox', '--disable-setuid-sandbox'],
965
+ const browser = await puppeteer.launch({
966
+ headless: "new", args: ['--no-sandbox', '--disable-setuid-sandbox'],
970
967
  warning: false,
971
968
  })
972
969
 
973
970
  try {
974
-
971
+
975
972
  route.url = route.url.replaceAll(/\/:[a-zA-Z0-9_-]+/gs, '')
976
973
  let page = await browser.newPage();
977
- await page.goto(`http://localhost:${port}/` , { waitUntil: 'networkidle2' });
974
+ await page.goto(`http://localhost:${port}/`, { waitUntil: 'networkidle2' });
978
975
  await page.waitForSelector('#root');
979
976
  await page.evaluate(() => {
980
977
  document.getElementById('meta').remove()
981
978
  document.querySelector('#isServer').innerHTML = 'window.isServer = false'
982
- if(document.head.getAttribute('prerender') === 'false'){
979
+ if (document.head.getAttribute('prerender') === 'false') {
983
980
  document.querySelector('#root').innerHTML = ''
984
981
  }
985
982
  })
986
983
  const html = await page.content();
987
-
984
+
988
985
  await page.close();
989
986
  await writer(process.cwd() + '/dist/' + (route.url === '/' ? 'index.html' : `${route.url}/` + 'index.html'), html)
990
987
  await browser.close();
@@ -992,11 +989,11 @@ async function Build() {
992
989
  console.log(error)
993
990
  await browser.close();
994
991
  }
995
- finally{
992
+ finally {
996
993
  await browser.close();
997
994
  server.close()
998
995
  }
999
-
996
+
1000
997
 
1001
998
  })
1002
999
 
@@ -1004,6 +1001,7 @@ async function Build() {
1004
1001
  globalThis.isBuilding = false
1005
1002
  clearTimeout(timeout)
1006
1003
  }, 1000)
1004
+ console.log(`Generated ${routes.length} html files for ${routes.length} routes`)
1007
1005
  }
1008
1006
 
1009
1007
  globalThis.routes = []
@@ -1041,75 +1039,72 @@ async function Build() {
1041
1039
 
1042
1040
  await writer(process.cwd() + "/dist/" + fileName.replace('.jsx', '.js'), data).then(async () => {
1043
1041
 
1044
- let { minify } = await import('terser')
1045
-
1046
- try {
1047
- let minified = await minify(data, {
1048
- toplevel: true,
1049
- ecma: 2016,
1050
- enclose: false,
1051
- module: true,
1052
- compress: true,
1053
- keep_fnames: true,
1054
1042
 
1055
- })
1056
1043
 
1057
- minified.code += `\n\n window.params = ${JSON.stringify(obj.url.split('/').filter((e) => e.includes(':')).map((e) => e.split(':')[1]))}`
1044
+ await writer(process.cwd() + "/dist/" + fileName.replace('.jsx', '.js'), data)
1058
1045
 
1059
- await writer(process.cwd() + "/dist/" + fileName.replace('.jsx', '.js'), minified.code)
1060
- } catch (error) {
1061
- console.log(error)
1062
- }
1063
1046
  })
1064
1047
 
1048
+ // configure routing for each page
1065
1049
 
1066
1050
  obj.compiledPath = process.cwd() + "/dist/pages/" + fileName.replace('.jsx', '.js')
1067
- let providerRedirects = {cloudflare: '_redirects', vercel: 'vercel.json', netlify:'_redirects'}
1068
- switch(true){
1051
+ let providerRedirects = { cloudflare: '_redirects', vercel: 'vercel.json', netlify: '_redirects' }
1052
+ switch (true) {
1069
1053
  case config && config.host && !config.host['_redirect']:
1070
1054
  let host = config.host.provider
1071
-
1072
- let provider = providerRedirects[host]
1073
- if(provider){
1074
-
1075
- let redirectFile = fs.existsSync(process.cwd() + '/dist/' + provider) ? fs.readFileSync(process.cwd() + '/dist/' + provider, 'utf8') : ''
1076
- let type = provider === '_redirects' ? 'text/plain' : 'application/json'
1077
-
1078
- let root = obj.url.includes(':') ? obj.url.split(':')[0] : obj.url
1079
- switch(true){
1055
+
1056
+ let provider = providerRedirects[host]
1057
+ if (provider) {
1058
+
1059
+ let redirectFile = null
1060
+ switch (true) {
1061
+ case provider === '_redirects':
1062
+ redirectFile = fs.existsSync(process.cwd() + '/dist/' + provider) ? fs.readFileSync(process.cwd() + '/dist/' + provider, 'utf8') : ''
1063
+ break;
1064
+ case provider === 'vercel.json':
1065
+ redirectFile = fs.existsSync(process.cwd() + '/' + provider) ? fs.readFileSync(process.cwd() + '/' + provider, 'utf8') : ''
1066
+ break;
1067
+ default:
1068
+ break;
1069
+ }
1070
+ let type = provider === '_redirects' ? 'text/plain' : 'application/json'
1071
+
1072
+ let root = obj.url.includes(':') ? obj.url.split('/:')[0] : obj.url
1073
+ switch (true) {
1080
1074
  case root === '/':
1081
1075
  break;
1082
1076
  case type === 'text/plain' && !redirectFile.includes(obj.url) && obj.url.includes(':'):
1083
1077
  let page = obj.pathname.split('/pages/')[1].replace('.jsx', '.js')
1084
1078
  redirectFile += `\n/${page} /${page} 200\n${obj.url} ${root} 200\n`
1085
1079
  !redirectFile.includes('/404') ? redirectFile += `\n/404 /404 404` : null
1086
- fs.writeFileSync(process.cwd() + '/dist/' + provider, redirectFile)
1080
+ fs.writeFileSync(process.cwd() + '/dist/' + provider, redirectFile)
1087
1081
  console.log(`Added ${obj.url} ${obj.url} 200 to ${provider}`)
1088
1082
  break;
1089
- case type === 'application/json' && !redirectFile.includes(root):
1090
- let json = JSON.parse(redirectFile) || {}
1083
+ case type === 'application/json' && !redirectFile?.includes(`${obj.url}`):
1084
+ let json = redirectFile ? JSON.parse(redirectFile) : {}
1091
1085
  let isVercel = provider === 'vercel.json' ? true : false
1092
- if(isVercel){
1086
+ if (isVercel) {
1093
1087
  json['rewrites'] = json['rewrites'] || []
1094
- json['rewrites'].push({ "source": `${root}/*`, "destination": `/${root}` })
1088
+ json['rewrites'].push({ "source": obj.url, "destination": `${root}/index.html` })
1089
+ fs.writeFileSync(process.cwd() + '/' + provider, JSON.stringify(json, null, 2))
1090
+ console.log(`Added ${obj.url} ${root}/index.html to ${provider}`)
1095
1091
  }
1096
- fs.writeFileSync(process.cwd() + '/dist/' + provider, JSON.stringify(json))
1097
- console.log(`Added ${root}/* ${root} 200 to ${provider}`)
1092
+
1098
1093
  }
1099
1094
  }
1100
1095
  break;
1101
1096
  case config && config.host && config.host['_redirect']:
1102
- let file = config.host['_redirect']
1097
+ let file = config.host['_redirect']
1103
1098
  file = file.split('./').join('')
1104
- let redirectFile = fs.existsSync(process.cwd() + '/' + file) ? fs.readFileSync(process.cwd() + '/' + file, 'utf8') : ''
1099
+ let redirectFile = fs.existsSync(process.cwd() + '/' + file) ? fs.readFileSync(process.cwd() + '/' + file, 'utf8') : ''
1105
1100
  fs.writeFileSync(process.cwd() + '/dist/' + file, redirectFile)
1106
1101
  console.log(`Using ${file} for redirects`)
1107
1102
  default:
1108
1103
  break;
1109
1104
 
1110
1105
  }
1111
-
1112
-
1106
+
1107
+
1113
1108
  globalThis.routes.push({ fileName: fileName, url: obj.url, html: '/' + (isBasePath ? 'index.html' : `${obj.url}/` + 'index.html') })
1114
1109
 
1115
1110
 
@@ -1151,18 +1146,7 @@ async function Build() {
1151
1146
  data = Compiler(data, process.cwd() + "/src/" + name);
1152
1147
 
1153
1148
  await writer(process.cwd() + "/dist/src/" + name.split('.jsx').join('.js'), data).then(async () => {
1154
- let { minify } = await import('terser')
1155
- try {
1156
- let minified = await minify(data, {
1157
- ecma: " 2016",
1158
- module: true,
1159
- compress: true,
1160
- keep_fnames: true,
1161
- })
1162
- await writer(process.cwd() + "/dist/src/" + name.replace('.jsx', '.js'), minified.code)
1163
- } catch (error) {
1164
- console.log(error)
1165
- }
1149
+ await writer(process.cwd() + "/dist/src/" + name.replace('.jsx', '.js'), data)
1166
1150
 
1167
1151
  })
1168
1152
  return
@@ -1221,11 +1205,13 @@ const s = () => {
1221
1205
 
1222
1206
  const server = http.createServer((req, res) => {
1223
1207
 
1224
- if (!req.url.endsWith('.js') && !req.url.endsWith('.css') && !req.url.endsWith('.mjs') && !req.url.endsWith('.cjs') && !req.url.endsWith('.html') && !req.url.endsWith('.json')) {
1225
- req.url = req.url !== '/' ? req.url.split('/')[1] : req.url
1226
- req.url = process.cwd() + '/dist/' + req.url + '/index.html'
1208
+ const validExtensions = ['.js', '.css', '.mjs', '.cjs', '.html', '.json', '.png', '.jpg', '.jpeg', '.gif', '.svg', '.mp4', '.webm', '.ogg'];
1209
+
1210
+ if (!validExtensions.some(ext => req.url.endsWith(ext))) {
1211
+ req.url = req.url !== '/' ? req.url.split('/')[1] : req.url;
1212
+ req.url = path.join(process.cwd(), 'dist', req.url, 'index.html');
1227
1213
  } else {
1228
- req.url = process.cwd() + '/dist/' + req.url
1214
+ req.url = path.join(process.cwd(), 'dist', req.url);
1229
1215
  }
1230
1216
 
1231
1217
  const filePath = req.url
@@ -1235,14 +1221,14 @@ const s = () => {
1235
1221
  res.writeHead(404, { 'Content-Type': 'text/html' });
1236
1222
  res.end(fs.existsSync(process.cwd() + '/dist/404') ? fs.readFileSync(process.cwd() + '/dist/404/index.html') : '404');
1237
1223
  } else {
1238
- const contentType = getContentType(filePath);
1224
+ const contentType = getContentType(filePath);
1239
1225
  switch (true) {
1240
1226
  case contentType === 'text/html' && globalThis.devMode:
1241
1227
  data = data.toString() + `<script type="module">
1242
- let ws = new WebSocket('ws://localhost:3000')
1228
+ let ws = new WebSocket('ws://localhost:${process.env.PORT || 3000}')
1243
1229
  ws.onmessage = (e) => {
1244
1230
  if(e.data === 'reload'){
1245
- window.rehydrate()
1231
+ window.location.reload()
1246
1232
  }
1247
1233
  }
1248
1234
  </script>
@@ -1257,18 +1243,44 @@ const s = () => {
1257
1243
 
1258
1244
  const ws = new WebSocketServer({ server });
1259
1245
  ws.on('connection', (socket) => {
1260
- console.log('Client connected');
1261
- socket.on('close', () => console.log('Client disconnected'));
1246
+ console.log('WebSocket Hydration Client connected');
1247
+ socket.on('close', () => console.log('WebSocket Hydration Client disconnected'));
1262
1248
  });
1263
1249
 
1264
1250
 
1265
1251
  function getContentType(filePath) {
1266
- if (filePath.includes('js')) {
1267
- return 'text/javascript';
1268
- } else if (filePath.includes('.css')) {
1269
- return 'text/css';
1270
- } else {
1271
- return 'text/html';
1252
+ let ext = ['.js', '.css', '.mjs', '.cjs', '.html', '.json', '.png', '.jpg', '.jpeg', '.gif', '.svg', '.mp4', '.webm', '.ogg'].includes(path.extname(filePath)) ? path.extname(filePath) : '.html'
1253
+ switch (ext) {
1254
+ case '.js':
1255
+ return 'text/javascript';
1256
+ case '.css':
1257
+ return 'text/css';
1258
+ case '.mjs':
1259
+ return 'text/javascript';
1260
+ case '.cjs':
1261
+ return 'text/javascript';
1262
+ case '.html':
1263
+ return 'text/html';
1264
+ case '.json':
1265
+ return 'application/json';
1266
+ case '.png':
1267
+ return 'image/png';
1268
+ case '.jpg':
1269
+ return 'image/jpg';
1270
+ case '.jpeg':
1271
+ return 'image/jpeg';
1272
+ case '.gif':
1273
+ return 'image/gif';
1274
+ case '.svg':
1275
+ return 'image/svg+xml';
1276
+ case '.mp4':
1277
+ return 'video/mp4';
1278
+ case '.webm':
1279
+ return 'video/webm';
1280
+ case '.ogg':
1281
+ return 'video/ogg';
1282
+ default:
1283
+ return 'application/octet-stream';
1272
1284
  }
1273
1285
  }
1274
1286
 
@@ -1279,21 +1291,20 @@ const s = () => {
1279
1291
  let i =
1280
1292
  setInterval(() => {
1281
1293
  if (globalThis.isBuilding && globalThis.devMode) {
1282
- // reload page
1283
- console.log(globalThis.isBuilding)
1284
- console.log('Reloading page...')
1294
+
1285
1295
  ws.clients.forEach((client) => {
1286
1296
  client.send('reload')
1287
- console.log('Reloaded page')
1288
1297
  })
1289
- }
1298
+ } else {
1299
+ clearInterval(i)
1300
+ }
1290
1301
  }, 120)
1291
1302
 
1292
1303
  }
1293
1304
 
1294
1305
 
1295
1306
  switch (true) {
1296
- case process.argv.includes('--watch'):
1307
+ case process.argv.includes('--watch') && !process.argv.includes('--build') && !process.argv.includes('--serve'):
1297
1308
 
1298
1309
  globalThis.devMode = true
1299
1310
  console.log(`
@@ -1323,7 +1334,7 @@ Vader.js v1.3.3
1323
1334
  globalThis.listen = true;
1324
1335
 
1325
1336
  break;
1326
- case process.argv.includes('--build'):
1337
+ case process.argv.includes('--build') && !process.argv.includes('--watch') && !process.argv.includes('--serve'):
1327
1338
  globalThis.devMode = false
1328
1339
  console.log(`
1329
1340
  Vader.js v1.3.3
@@ -1332,7 +1343,7 @@ Building to ./dist
1332
1343
  Build()
1333
1344
 
1334
1345
  break;
1335
- case process.argv.includes('--serve'):
1346
+ case process.argv.includes('--serve') && !process.argv.includes('--watch') && !process.argv.includes('--build'):
1336
1347
  let port = process.argv[process.argv.indexOf('--serve') + 1] || 3000
1337
1348
  process.env.PORT = port
1338
1349
  globalThis.devMode = false
@@ -1350,9 +1361,9 @@ Vader.js is a reactive framework for building interactive applications for the w
1350
1361
  Usage: vader <command>
1351
1362
 
1352
1363
  Commands:
1353
- --watch Watch the pages folder for changes and recompile
1364
+ --watch Watch the pages folder for changes with hot reloading
1354
1365
 
1355
- --build Build the project
1366
+ --build Build the project to ./dist
1356
1367
 
1357
1368
  --serve Serve the project on a port (default 3000)
1358
1369