vaderjs 1.3.3-alpha-151 → 1.3.3-alpha-160
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 +9 -5
- package/package.json +5 -4
- package/runtime/router.js +1 -1
- package/runtime/vader.js +1 -1
- package/vader.js +331 -375
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
[](https://github.com/Postr-Inc/Vader.js/blob/main/LICENSE) [](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={(
|
|
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 {
|
|
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
|
-
|
|
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={(
|
|
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-
|
|
5
|
+
"version": "1.3.3-alpha-160",
|
|
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
|
-
"
|
|
32
|
-
"
|
|
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
|
|
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){(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(",,",",");return this.functions.find((e=>e.ref===s))||document.addEventListener(`$dispatch_#id=${s}`,(t=>{let{name:r,event:n}=t.detail;if(r===s){let t=this.functions.find((e=>e.ref===s)).params;Object.keys(t).forEach((e=>{t[e]instanceof CustomEvent&&delete t[e],void 0===t[e]?delete t[e]:t[e]}));let r=n;console.log(r),e(n.detail.event)}})),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)=>{console.log(t),this.state[e]=t,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 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
|
@@ -4,20 +4,26 @@ 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 =
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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);
|
|
20
21
|
|
|
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')
|
|
@@ -31,47 +37,16 @@ if (typeof process.env.isCloudflare !== "undefined" || !fs.existsSync(process.cw
|
|
|
31
37
|
fs.writeFileSync(process.cwd() + "/dist/index.html", htmlFile)
|
|
32
38
|
}
|
|
33
39
|
|
|
34
|
-
|
|
35
40
|
|
|
36
|
-
function Compiler(func, file) {
|
|
37
|
-
let string = func;
|
|
38
|
-
// Remove block comments
|
|
39
41
|
|
|
42
|
+
function Compiler(func, file) {
|
|
43
|
+
let string = func;
|
|
40
44
|
let returns = []
|
|
41
45
|
let comments = string.match(/\{\s*\/\*.*\*\/\s*}/gs)?.map((comment) => comment.trim());
|
|
42
46
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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,40 +118,40 @@ function Compiler(func, file) {
|
|
|
98
118
|
const attributeRegex =
|
|
99
119
|
/\s*([a-zA-Z0-9_-]+)(\s*=\s*("([^"\\]*(\\.[^"\\]*)*)"|'([^'\\]*(\\.[^'\\]*)*)'|\{(?:[^{}]|(?:\{(?:[^{}]|(?:\{[^{}]*\})*)*\})*)*\}|(?:\([^)]*\)|\{[^}]*\}|()=>\s*(?:\{[^}]*\})?)|\[[^\]]*\]))?/gs;
|
|
100
120
|
|
|
101
|
-
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
|
|
102
124
|
const functionAttributeRegex = /\s*([a-zA-Z0-9_-]+)(\s*=\s*{((?:[^{}]|(?:\{(?:[^{}]|(?:\{[^{}]*\})*)*\})*)*)})/gs;
|
|
103
125
|
|
|
104
126
|
let attributesList = [];
|
|
105
127
|
|
|
106
|
-
|
|
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) {
|
|
144
|
+
|
|
110
145
|
let [, attributeName, attributeValue] = functionMatch;
|
|
111
146
|
let attribute = {};
|
|
112
147
|
|
|
113
|
-
if (attributeValue && attributeValue.includes("=>") || attributeValue && attributeValue.includes("function")
|
|
114
|
-
|
|
115
|
-
|
|
148
|
+
if (attributeValue && attributeValue.includes("=>") || attributeValue && attributeValue.includes("function")
|
|
149
|
+
&& !spreadFunctions.includes(attributeValue)
|
|
150
|
+
) {
|
|
151
|
+
|
|
116
152
|
let ref = Math.random().toString(36).substring(2).split('').filter((e) => !Number(e)).join('')
|
|
117
153
|
let old = `${attributeName}${attributeValue}`
|
|
118
|
-
|
|
119
|
-
string.split("\n").forEach((line) => {
|
|
120
|
-
if (line.includes(name) && line.includes("function")) {
|
|
121
|
-
line = line.trim();
|
|
122
|
-
line = line.replace(/\s+/g, " ");
|
|
123
|
-
|
|
124
|
-
let ps = line.split("(").slice(1).join("(").split(")")[0].trim();
|
|
125
|
-
|
|
126
|
-
// remove comments
|
|
127
|
-
ps = ps.match(/\/\*.*\*\//gs)
|
|
128
|
-
? ps.replace(ps.match(/\/\*.*\*\//gs)[0], "")
|
|
129
|
-
: ps;
|
|
130
|
-
functionparams.push({ ref: ref, name: name, params: ps });
|
|
131
|
-
|
|
132
|
-
}
|
|
133
|
-
});
|
|
134
|
-
});
|
|
154
|
+
|
|
135
155
|
let elementMatch = string.match(/<([a-zA-Z0-9_-]+)([^>]*)>/gs);
|
|
136
156
|
let isJSXComponent = false;
|
|
137
157
|
elementMatch.forEach((element) => {
|
|
@@ -143,7 +163,7 @@ function Compiler(func, file) {
|
|
|
143
163
|
.split(" ")[0];
|
|
144
164
|
isJSXComponent = elementTag.match(/^[A-Z]/) ? true : false;
|
|
145
165
|
}
|
|
146
|
-
});
|
|
166
|
+
});
|
|
147
167
|
// add ; after newlines
|
|
148
168
|
|
|
149
169
|
|
|
@@ -185,18 +205,17 @@ function Compiler(func, file) {
|
|
|
185
205
|
newvalue = newvalue.replace(/}\s*$/, '');
|
|
186
206
|
|
|
187
207
|
|
|
188
|
-
|
|
189
|
-
functionparams.length > 0 ? params = params + ',' + functionparams.map((e) => e.name).join(',') : null
|
|
208
|
+
|
|
190
209
|
|
|
191
210
|
newvalue = newvalue.replaceAll(',,', ',')
|
|
192
211
|
let paramnames = params ? params.split(',').map((e) => e.trim()) : null
|
|
193
212
|
paramnames = paramnames ? paramnames.filter((e) => e.length > 0) : null
|
|
194
213
|
// remove comments
|
|
195
214
|
paramnames = paramnames ? paramnames.map((e) => e.match(/\/\*.*\*\//gs) ? e.replace(e.match(/\/\*.*\*\//gs)[0], "") : e) : null
|
|
196
|
-
|
|
215
|
+
|
|
197
216
|
// add ; after newlines
|
|
198
217
|
newvalue = newvalue.replaceAll(/\n/g, ";\n")
|
|
199
|
-
|
|
218
|
+
|
|
200
219
|
let bind = isJSXComponent ? `${attributeName}='function(${params}){${newvalue}}'` : `${attributeName}="\$\{this.bind(function(){${newvalue}}.bind(this), ${isJSXComponent}, "${ref}", "${paramnames ? paramnames.map((e, index) => {
|
|
201
220
|
if (e.length < 1) return ''
|
|
202
221
|
if (e.length > 0) {
|
|
@@ -204,11 +223,15 @@ function Compiler(func, file) {
|
|
|
204
223
|
}
|
|
205
224
|
return e
|
|
206
225
|
}) : ''}" ${params ? params.split(',').map((e) => e.trim()).filter(Boolean).map((e) => `,${e}`).join('') : ''})}"`
|
|
207
|
-
|
|
226
|
+
|
|
208
227
|
string = string.replace(old, bind);
|
|
209
228
|
}
|
|
210
229
|
}
|
|
211
230
|
|
|
231
|
+
/**
|
|
232
|
+
* @search - handle attributes for html elements
|
|
233
|
+
* @keywords - attributes, props, html attributes
|
|
234
|
+
*/
|
|
212
235
|
let match;
|
|
213
236
|
while ((match = elementRegex.exec(code)) !== null) {
|
|
214
237
|
let [, element, attributes] = match;
|
|
@@ -230,34 +253,46 @@ function Compiler(func, file) {
|
|
|
230
253
|
|
|
231
254
|
function extractOuterReturn(code) {
|
|
232
255
|
// match return [...]
|
|
233
|
-
let returns = code.match(/return\s
|
|
256
|
+
let returns = code.match(/return\s*\<>.*\<\/>|return\s*\(.*\)/gs);
|
|
234
257
|
|
|
235
258
|
return returns || [];
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
if (string.match(/return\s*\<>/gs) && !string.match(/return\s*\<>.*\<\/>/gs)
|
|
259
|
+
}
|
|
260
|
+
if (string.match(/return\s*\<>|return\s*\(.*\)/gs) && !string.match(/return\s*\<>.*\<\/>|return\s*\(.*\)/gs)
|
|
239
261
|
|| string.match(/return\s*\<[a-zA-Z0-9_-]+.*>/gs)
|
|
240
262
|
) {
|
|
241
|
-
|
|
263
|
+
|
|
264
|
+
try {
|
|
265
|
+
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)
|
|
266
|
+
} catch (error) {
|
|
267
|
+
console.error(error)
|
|
268
|
+
process.exit(1)
|
|
269
|
+
}
|
|
242
270
|
}
|
|
243
271
|
|
|
244
272
|
let outerReturn = extractOuterReturn(string);
|
|
245
273
|
let contents = "";
|
|
246
274
|
let updatedContents = "";
|
|
275
|
+
/**
|
|
276
|
+
* @search - handle return [...]
|
|
277
|
+
* @keywords - return, return jsx, return html, return [...]
|
|
278
|
+
*/
|
|
247
279
|
outerReturn.forEach((returnStatement) => {
|
|
248
280
|
|
|
249
281
|
let lines = returnStatement.split("\n");
|
|
250
282
|
|
|
251
283
|
for (let i = 0; i < lines.length; i++) {
|
|
252
284
|
let line = lines[i];
|
|
253
|
-
if (line.match(/return\s
|
|
285
|
+
if (line.match(/return\s*\<>|return\s*\(/gs)) {
|
|
254
286
|
continue;
|
|
255
287
|
}
|
|
256
288
|
contents += line + "\n";
|
|
257
289
|
}
|
|
290
|
+
let usesBraces = returnStatement.match(/return\s*\(/gs) ? true : false;
|
|
258
291
|
|
|
259
|
-
|
|
260
|
-
contents = contents.trim().replace(/\]$/, "")
|
|
292
|
+
|
|
293
|
+
contents = contents.trim().replace(/\]$/, "")
|
|
294
|
+
contents = contents.replace(/\)$/, "");
|
|
295
|
+
usesBraces ? !contents.includes('<>') ? contents = `<>${contents}</>` : null : null
|
|
261
296
|
updatedContents = contents;
|
|
262
297
|
let attributes = extractAttributes(contents);
|
|
263
298
|
|
|
@@ -283,6 +318,7 @@ function Compiler(func, file) {
|
|
|
283
318
|
? value = `{this.parseStyle({${value.split('{{')[1].split('}}')[0]}})}` : null
|
|
284
319
|
|
|
285
320
|
|
|
321
|
+
|
|
286
322
|
value = `="\$${value}",`;
|
|
287
323
|
string = string.replace(oldvalue, value);
|
|
288
324
|
|
|
@@ -294,34 +330,13 @@ function Compiler(func, file) {
|
|
|
294
330
|
|
|
295
331
|
}
|
|
296
332
|
} else if (value && value.new) {
|
|
297
|
-
string = string.replace(
|
|
333
|
+
string = string.replace(oldvalue, value.new);
|
|
298
334
|
}
|
|
299
335
|
}
|
|
300
336
|
});
|
|
301
337
|
});
|
|
302
338
|
|
|
303
|
-
let retursnString = [];
|
|
304
|
-
let outerReturnString = extractOuterReturn(string);
|
|
305
|
-
|
|
306
|
-
outerReturnString.forEach((returnStatement) => {
|
|
307
|
-
let lines = returnStatement.split("\n");
|
|
308
|
-
let code = "";
|
|
309
|
-
for (let i = 0; i < lines.length; i++) {
|
|
310
|
-
let line = lines[i];
|
|
311
|
-
|
|
312
|
-
code += line + "\n";
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
code = code.trim().replace(/\<\/\>$/, "");
|
|
316
|
-
retursnString.push(code);
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
retursnString.forEach((returnStatement, index) => {
|
|
320
|
-
let old = outerReturnString[index];
|
|
321
339
|
|
|
322
|
-
let newReturn = `${returnStatement}</>`;
|
|
323
|
-
string = string.replace(old, newReturn);
|
|
324
|
-
});
|
|
325
340
|
|
|
326
341
|
if (comments) {
|
|
327
342
|
comments.forEach((comment) => {
|
|
@@ -358,9 +373,7 @@ function Compiler(func, file) {
|
|
|
358
373
|
valuestate = valuestate.match(regex) ? valuestate.match(regex)[0].split("useState(")[1].split(")")[0].trim() : valuestate
|
|
359
374
|
|
|
360
375
|
|
|
361
|
-
let newState = `${varType} [${key}, ${setKey}] = this.useState('${key}', ${valuestate}
|
|
362
|
-
|
|
363
|
-
`;
|
|
376
|
+
let newState = `${varType} [${key}, ${setKey}] = this.useState('${key}', ${valuestate}`;
|
|
364
377
|
string = string.replace(line, newState);
|
|
365
378
|
break;
|
|
366
379
|
case line.includes("useRef") && !line.includes("import"):
|
|
@@ -405,194 +418,100 @@ function Compiler(func, file) {
|
|
|
405
418
|
string = string.replaceAll('../src', './src')
|
|
406
419
|
|
|
407
420
|
function parseComponents(body, isChild) {
|
|
408
|
-
let componentRegex = /<([A-Z][A-Za-z0-9_-]+)([^>]*)
|
|
421
|
+
let componentRegex = /<([A-Z][A-Za-z0-9_-]+)\s*([^>]*)>\s*([\s\S]*?)\s*<\/\1>|<([A-Z][A-Za-z0-9_-]+)([^]*?)\/>/gs;
|
|
409
422
|
|
|
410
423
|
let componentMatch = body.match(componentRegex);
|
|
411
424
|
let topComponent = "";
|
|
412
425
|
componentMatch?.forEach(async (component) => {
|
|
413
|
-
|
|
426
|
+
|
|
414
427
|
let [, element, attributes] = component;
|
|
415
|
-
|
|
428
|
+
let before = component;
|
|
429
|
+
component = component.trim().replace(/\s+/g, " ");
|
|
416
430
|
|
|
417
431
|
!isChild ? (topComponent = component) : null;
|
|
418
|
-
|
|
432
|
+
|
|
419
433
|
|
|
420
434
|
let myChildrens = [];
|
|
421
435
|
|
|
422
436
|
let name = component.split("<")[1].split(">")[0].split(" ")[0].replace("/", "");
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
437
|
+
let componentAttributes = component.split("<")[1].split(">")[0].split(" ").join(" ").replace(name, "").trim();
|
|
438
|
+
const dynamicAttributesRegex = /(\w+)(?:="([^"]*)")?(?:='([^']*)')?(?:=\{([^}]*)\})?(?:=\{(.*?)\})?(?:={([^}]*)})?(?:{([^}]*)})?|(?:{(?:[^{}]+|\{(?:[^{}]+|\{[^}]*\})*\})*\})|(\.{3}\{(?:[^{}]+|\{(?:[^{}]+|\{[^}]*\})*\})*\})|\$=\{(?:[^{}]+|\{(?:[^{}]+|\{[^}]*\})*\})*\}/gs;
|
|
439
|
+
|
|
426
440
|
|
|
427
441
|
|
|
428
442
|
|
|
429
443
|
|
|
430
|
-
|
|
431
444
|
let props = component.match(dynamicAttributesRegex)
|
|
432
|
-
|
|
433
|
-
let filteredProps = [];
|
|
434
|
-
let isWithinComponent = false;
|
|
435
|
-
let componentName = name
|
|
436
|
-
|
|
437
|
-
for (let prop of props) {
|
|
438
|
-
|
|
439
|
-
if (prop.includes(componentName)) {
|
|
440
|
-
// If the component is encountered, start collecting props
|
|
441
|
-
isWithinComponent = true;
|
|
442
|
-
filteredProps.push(prop);
|
|
443
|
-
} else if (isWithinComponent && prop.includes('=')) {
|
|
444
|
-
|
|
445
|
-
if(prop.includes('${')){
|
|
446
|
-
// if it has an object inside of it then we should just do soemting:object else we should do something: `${object}`
|
|
447
|
-
|
|
448
|
-
prop = prop.replace('="', ':').replace('}"', '}')
|
|
449
|
-
if(prop.includes('${')){
|
|
450
|
-
prop = prop.replace('="',':')
|
|
451
|
-
prop = prop.replace('${', '')
|
|
452
|
-
prop = prop.replace('}', '')
|
|
453
|
-
|
|
454
|
-
}
|
|
455
|
-
if(prop.includes('="${{')){
|
|
456
|
-
prop = prop.replace('${{', '{')
|
|
457
|
-
prop = prop.replace('}}', '}')
|
|
458
|
-
prop = prop.replace('="', ':')
|
|
459
|
-
prop = prop.replace('}"', '}')
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
}
|
|
463
|
-
if(prop.startsWith('={')){
|
|
464
|
-
prop = prop.replace('={', ':`${')
|
|
465
|
-
prop.replace('} ', '}`')
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
if(prop.includes('function')){
|
|
469
|
-
// parse 'function' to function
|
|
470
|
-
prop = prop.replace("'", '')
|
|
471
|
-
|
|
472
|
-
if(prop.endsWith("}'")){
|
|
473
|
-
prop = prop.replace("}'", '}')
|
|
474
|
-
console
|
|
475
|
-
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
prop = prop.replace('=function', ':function')
|
|
479
|
-
}
|
|
480
445
|
|
|
481
|
-
filteredProps
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
446
|
+
let filteredProps = [];
|
|
447
|
+
let isWithinComponent = false;
|
|
448
|
+
let componentName = name
|
|
449
|
+
let currentProps = []
|
|
450
|
+
|
|
451
|
+
let $_ternaryprops = []
|
|
486
452
|
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
// Split the spreadObject by commas and process each part individually
|
|
499
|
-
const parts = spreadObject.split(',').map((part) => {
|
|
500
|
-
if (part.trim().startsWith('{') && part.trim().endsWith('}')) {
|
|
501
|
-
// Handle nested spreads within the object
|
|
502
|
-
const nestedParts = part
|
|
503
|
-
.trim()
|
|
504
|
-
.slice(1, -1) // Remove outer {}
|
|
505
|
-
.split(',')
|
|
506
|
-
.map((nestedPart) => {
|
|
507
|
-
return nestedPart.includes('...') ? nestedPart.trim().replace(/\.\.\./, '') : `...${nestedPart.trim()}`;
|
|
508
|
-
});
|
|
509
|
-
return `{${nestedParts.join(',')}}`;
|
|
510
|
-
} else {
|
|
511
|
-
return part.includes('...') ? part.trim() : part.trim().startsWith('{') ? `...${part.trim()}` : `${part.trim()}`;
|
|
512
|
-
}
|
|
513
|
-
});
|
|
514
|
-
if(!parts.join(',').includes('{(')){
|
|
515
|
-
|
|
516
|
-
processedSpreadObject = `${parts.join(',')}`
|
|
517
|
-
|
|
518
|
-
}else{
|
|
519
|
-
let prop = parts.join(',')
|
|
520
|
-
prop = prop.replaceAll('{(', '(')
|
|
521
|
-
prop = prop.replaceAll(')}', ')')
|
|
522
|
-
processedSpreadObject = prop
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
} else {
|
|
527
|
-
// Process nested structures within the object
|
|
528
|
-
processedSpreadObject = `{...${spreadObject}}`;
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
const $propsKey = `$props_${Math.random().toString(36).substring(2)}`;
|
|
532
|
-
filteredProps.push(`${$propsKey}:${processedSpreadObject}`);
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
else{
|
|
537
|
-
isWithinComponent = false;
|
|
538
|
-
}
|
|
539
|
-
}
|
|
453
|
+
component = component.replaceAll(/\s+/g, " ");
|
|
454
|
+
|
|
455
|
+
component = component.replace(componentAttributes, '')
|
|
456
|
+
$_ternaryprops.forEach((prop) => {
|
|
457
|
+
component = component.replace(prop, '')
|
|
458
|
+
})
|
|
459
|
+
|
|
460
|
+
let children = component.split(`<${name}`)[1].split(`</${name}>`)[0].trim().replace(/\s+/g, " ").trim().replace(/,$/, '').replace('>', '').replace(/\/$/, '').trim()
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
props = filteredProps.join(',').replace(/\s+/g, " ").trim().replace(/,$/, '')
|
|
540
464
|
|
|
541
|
-
// get inner content of <Component>inner content</Component>
|
|
542
|
-
let children = new RegExp(`<${name}[^>]*>(.*?)<\/${name}>`, "gs").exec(component) ? new RegExp(`<${name}[^>]*>(.*?)<\/${name}>`, "gs").exec(component)[1] : null;
|
|
543
|
-
|
|
544
|
-
props = filteredProps.join(',')
|
|
545
|
-
|
|
546
465
|
let savedname = name;
|
|
547
|
-
|
|
548
|
-
|
|
466
|
+
|
|
467
|
+
|
|
549
468
|
|
|
550
469
|
name = name + Math.random().toString(36).substring(2);
|
|
551
470
|
if (children && children.match(componentRegex)) {
|
|
552
471
|
children = parseComponents(children, true);
|
|
553
472
|
childs.push({ parent: name, children: children });
|
|
554
473
|
} else {
|
|
555
|
-
|
|
474
|
+
|
|
556
475
|
children ? childs.push({ parent: name, children: children }) : null;
|
|
557
476
|
}
|
|
558
477
|
|
|
559
478
|
childs.forEach((child) => {
|
|
560
479
|
if (child.parent == name) {
|
|
561
480
|
let html = child.children.match(
|
|
562
|
-
/<([
|
|
481
|
+
/<([A-Z][A-Za-z0-9_-]+)([^>]*)>(.*?)<\/\1>|<([A-Z][A-Za-z0-9_-]+)([^]*?)\/>/gs
|
|
563
482
|
);
|
|
564
483
|
if (html) {
|
|
565
484
|
html = html.map((h) => h.trim().replace(/\s+/g, " ")).join(" ");
|
|
566
|
-
let before = child.children;
|
|
567
485
|
child.children = child.children.replaceAll(html, `${html}`);
|
|
568
486
|
// remove duplicate quotes
|
|
569
487
|
}
|
|
570
488
|
|
|
571
489
|
myChildrens.push(child.children);
|
|
490
|
+
childs = childs.filter((e) => e.parent !== name);
|
|
572
491
|
}
|
|
573
492
|
});
|
|
574
493
|
|
|
575
494
|
|
|
576
495
|
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
496
|
+
|
|
497
|
+
props = props.replaceAll(`,${savedname}`, '').replaceAll(savedname, '')
|
|
498
|
+
if (props.startsWith(',')) {
|
|
499
|
+
props = props.replace(',', '')
|
|
500
|
+
}
|
|
501
|
+
props = props.replaceAll("='", ":'")
|
|
502
|
+
.replaceAll('=`', ':`')
|
|
584
503
|
.replaceAll('="', ':"')
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
.replaceAll('={', ":`")
|
|
588
|
-
|
|
504
|
+
.replaceAll('={', ':')
|
|
505
|
+
|
|
589
506
|
|
|
590
507
|
/**
|
|
591
508
|
* @memoize - memoize a component to be remembered on each render and replace the old jsx
|
|
592
509
|
*/
|
|
593
510
|
|
|
594
|
-
|
|
595
|
-
replace =
|
|
511
|
+
|
|
512
|
+
let replace = "";
|
|
513
|
+
replace = `\${this.memoize(this.createComponent(${savedname}, {${props}}, [\`${myChildrens.join('')}\`]))}`;
|
|
514
|
+
|
|
596
515
|
|
|
597
516
|
body = body.replace(before, replace);
|
|
598
517
|
});
|
|
@@ -600,12 +519,61 @@ function Compiler(func, file) {
|
|
|
600
519
|
return body;
|
|
601
520
|
}
|
|
602
521
|
|
|
522
|
+
string = string.replaceAll('vaderjs/client', '/vader.js')
|
|
523
|
+
|
|
524
|
+
const importRegex = /import\s*([^\s,]+|\{[^}]+\})\s*from\s*(['"])(.*?)\2/g;
|
|
525
|
+
const imports = string.match(importRegex);
|
|
526
|
+
let replaceMents = [];
|
|
603
527
|
|
|
604
528
|
|
|
605
|
-
|
|
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
|
+
}
|
|
606
538
|
|
|
607
|
-
|
|
608
|
-
|
|
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
|
+
}
|
|
609
577
|
|
|
610
578
|
string = string.replaceAll(/\$\{[^{]*\.{3}/gs, (match) => {
|
|
611
579
|
if (match.includes('...')) {
|
|
@@ -658,7 +626,8 @@ function Compiler(func, file) {
|
|
|
658
626
|
if (line.includes('import')) {
|
|
659
627
|
// Regular expression for matching import() statements
|
|
660
628
|
let asyncimportMatch = line.match(/import\s*\((.*)\)/gs);
|
|
661
|
-
|
|
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);
|
|
662
631
|
|
|
663
632
|
if (asyncimportMatch) {
|
|
664
633
|
asyncimportMatch.forEach(async (match) => {
|
|
@@ -737,6 +706,10 @@ function Compiler(func, file) {
|
|
|
737
706
|
case path && path.includes('.css'):
|
|
738
707
|
string = string.replace(beforeimport, '')
|
|
739
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
|
+
|
|
740
713
|
break;
|
|
741
714
|
default:
|
|
742
715
|
let beforePath = path
|
|
@@ -781,6 +754,12 @@ function Compiler(func, file) {
|
|
|
781
754
|
|
|
782
755
|
globalThis.isBuilding = false
|
|
783
756
|
globalThis.isWriting = null
|
|
757
|
+
const glb = await glob("**/**/**/**.{jsx,js}", {
|
|
758
|
+
ignore: ["node_modules/**/*", "dist/**/*"],
|
|
759
|
+
cwd: process.cwd() + '/pages/',
|
|
760
|
+
absolute: true,
|
|
761
|
+
recursive: true
|
|
762
|
+
});
|
|
784
763
|
async function Build() {
|
|
785
764
|
globalThis.isBuilding = true
|
|
786
765
|
console.log('Compiling......')
|
|
@@ -789,33 +768,7 @@ async function Build() {
|
|
|
789
768
|
return text;
|
|
790
769
|
};
|
|
791
770
|
|
|
792
|
-
|
|
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
|
-
const glb = await glob("**/**/**/**.{jsx,js}", {
|
|
810
|
-
ignore: ["node_modules/**/*", "dist/**/*"],
|
|
811
|
-
cwd: process.cwd() + '/pages/',
|
|
812
|
-
absolute: true,
|
|
813
|
-
recursive: true
|
|
814
|
-
});
|
|
815
|
-
|
|
816
|
-
// Process files in the 'pages' directory
|
|
817
|
-
let appjs = '';
|
|
818
|
-
let hasWritten = []
|
|
771
|
+
|
|
819
772
|
function ssg(routes = []) {
|
|
820
773
|
globalThis.isBuilding = true
|
|
821
774
|
console.log(`Generating html files for ${routes.length} routes`)
|
|
@@ -823,10 +776,10 @@ async function Build() {
|
|
|
823
776
|
if (route.url.includes(':')) {
|
|
824
777
|
console.log('Route ' + route.url + ' is a dynamic route and will not be generated')
|
|
825
778
|
return
|
|
826
|
-
}
|
|
827
|
-
let equalparamroute = routes.map((e) => {
|
|
779
|
+
}
|
|
780
|
+
let equalparamroute = routes.map((e) => {
|
|
828
781
|
if (e.url.includes(':')) {
|
|
829
|
-
let url = e.url.split('/:')[0]
|
|
782
|
+
let url = e.url.split('/:')[0]
|
|
830
783
|
if (url && route.url === url) {
|
|
831
784
|
return e
|
|
832
785
|
} else {
|
|
@@ -910,14 +863,14 @@ async function Build() {
|
|
|
910
863
|
res.render(module, req, res, module.$metadata)
|
|
911
864
|
})
|
|
912
865
|
${equalparamroute.length > 0 ? equalparamroute.map((e) => {
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
866
|
+
|
|
867
|
+
|
|
868
|
+
|
|
869
|
+
return `router.get('${e.url}', async (req, res) => {
|
|
917
870
|
let module = await import('/${e.fileName.replace('.jsx', '.js')}')
|
|
918
871
|
res.render(module, req, res, module.$metadata)
|
|
919
872
|
})\n`
|
|
920
|
-
|
|
873
|
+
}) : ''}
|
|
921
874
|
router.listen(3000)
|
|
922
875
|
|
|
923
876
|
</script>
|
|
@@ -934,8 +887,8 @@ async function Build() {
|
|
|
934
887
|
let port = Math.floor(Math.random() * (65535 - 49152 + 1) + 49152)
|
|
935
888
|
|
|
936
889
|
const server = http.createServer((req, res) => {
|
|
937
|
-
|
|
938
|
-
if (req.url === '/') {
|
|
890
|
+
|
|
891
|
+
if (req.url === '/') {
|
|
939
892
|
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
940
893
|
res.end(document);
|
|
941
894
|
} else {
|
|
@@ -967,38 +920,54 @@ async function Build() {
|
|
|
967
920
|
|
|
968
921
|
globalThis.listen = true;
|
|
969
922
|
|
|
970
|
-
const browser
|
|
971
|
-
headless:
|
|
923
|
+
const browser = await puppeteer.launch({
|
|
924
|
+
headless: "new", args: ['--no-sandbox', '--disable-setuid-sandbox'],
|
|
972
925
|
warning: false,
|
|
973
926
|
})
|
|
974
|
-
|
|
927
|
+
|
|
928
|
+
const browserPID = browser.process().pid
|
|
975
929
|
try {
|
|
976
|
-
|
|
930
|
+
|
|
977
931
|
route.url = route.url.replaceAll(/\/:[a-zA-Z0-9_-]+/gs, '')
|
|
978
932
|
let page = await browser.newPage();
|
|
979
|
-
await page.goto(`http://localhost:${port}
|
|
980
|
-
await page.
|
|
933
|
+
await page.goto(`http://localhost:${port}/`, { waitUntil: 'networkidle2' });
|
|
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 })
|
|
981
943
|
await page.evaluate(() => {
|
|
982
944
|
document.getElementById('meta').remove()
|
|
983
945
|
document.querySelector('#isServer').innerHTML = 'window.isServer = false'
|
|
984
|
-
if(document.head.getAttribute('prerender') === 'false'){
|
|
946
|
+
if (document.head.getAttribute('prerender') === 'false') {
|
|
985
947
|
document.querySelector('#root').innerHTML = ''
|
|
948
|
+
console.log(`Disabled prerendering for ${window.location.pathname}`)
|
|
986
949
|
}
|
|
987
|
-
})
|
|
950
|
+
})
|
|
988
951
|
const html = await page.content();
|
|
989
|
-
|
|
952
|
+
|
|
990
953
|
await page.close();
|
|
991
954
|
await writer(process.cwd() + '/dist/' + (route.url === '/' ? 'index.html' : `${route.url}/` + 'index.html'), html)
|
|
992
955
|
await browser.close();
|
|
956
|
+
server.close()
|
|
957
|
+
|
|
993
958
|
} catch (error) {
|
|
994
|
-
|
|
959
|
+
server.close()
|
|
995
960
|
await browser.close();
|
|
996
961
|
}
|
|
997
|
-
finally{
|
|
962
|
+
finally {
|
|
998
963
|
await browser.close();
|
|
999
964
|
server.close()
|
|
1000
965
|
}
|
|
1001
|
-
|
|
966
|
+
try {
|
|
967
|
+
process.kill(browserPID)
|
|
968
|
+
} catch (error) {
|
|
969
|
+
}
|
|
970
|
+
|
|
1002
971
|
|
|
1003
972
|
})
|
|
1004
973
|
|
|
@@ -1006,6 +975,7 @@ async function Build() {
|
|
|
1006
975
|
globalThis.isBuilding = false
|
|
1007
976
|
clearTimeout(timeout)
|
|
1008
977
|
}, 1000)
|
|
978
|
+
console.log(`Generated ${routes.length} html files for ${routes.length} routes`)
|
|
1009
979
|
}
|
|
1010
980
|
|
|
1011
981
|
globalThis.routes = []
|
|
@@ -1040,78 +1010,74 @@ async function Build() {
|
|
|
1040
1010
|
data = Compiler(data, origin);
|
|
1041
1011
|
|
|
1042
1012
|
|
|
1043
|
-
|
|
1044
1013
|
await writer(process.cwd() + "/dist/" + fileName.replace('.jsx', '.js'), data).then(async () => {
|
|
1045
1014
|
|
|
1046
|
-
let { minify } = await import('terser')
|
|
1047
|
-
|
|
1048
|
-
try {
|
|
1049
|
-
let minified = await minify(data, {
|
|
1050
|
-
toplevel: true,
|
|
1051
|
-
ecma: 2016,
|
|
1052
|
-
enclose: false,
|
|
1053
|
-
module: true,
|
|
1054
|
-
compress: true,
|
|
1055
|
-
keep_fnames: true,
|
|
1056
1015
|
|
|
1057
|
-
})
|
|
1058
1016
|
|
|
1059
|
-
|
|
1017
|
+
await writer(process.cwd() + "/dist/" + fileName.replace('.jsx', '.js'), data)
|
|
1060
1018
|
|
|
1061
|
-
await writer(process.cwd() + "/dist/" + fileName.replace('.jsx', '.js'), minified.code)
|
|
1062
|
-
} catch (error) {
|
|
1063
|
-
console.log(error)
|
|
1064
|
-
}
|
|
1065
1019
|
})
|
|
1066
1020
|
|
|
1021
|
+
// configure routing for each page
|
|
1067
1022
|
|
|
1068
1023
|
obj.compiledPath = process.cwd() + "/dist/pages/" + fileName.replace('.jsx', '.js')
|
|
1069
|
-
let providerRedirects = {cloudflare: '_redirects', vercel: 'vercel.json', netlify:'_redirects'}
|
|
1070
|
-
switch(true){
|
|
1024
|
+
let providerRedirects = { cloudflare: '_redirects', vercel: 'vercel.json', netlify: '_redirects' }
|
|
1025
|
+
switch (true) {
|
|
1071
1026
|
case config && config.host && !config.host['_redirect']:
|
|
1072
1027
|
let host = config.host.provider
|
|
1073
|
-
|
|
1074
|
-
let provider = providerRedirects[host]
|
|
1075
|
-
if(provider){
|
|
1076
|
-
|
|
1077
|
-
let redirectFile =
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1028
|
+
|
|
1029
|
+
let provider = providerRedirects[host]
|
|
1030
|
+
if (provider) {
|
|
1031
|
+
|
|
1032
|
+
let redirectFile = null
|
|
1033
|
+
switch (true) {
|
|
1034
|
+
case provider === '_redirects':
|
|
1035
|
+
redirectFile = fs.existsSync(process.cwd() + '/dist/' + provider) ? fs.readFileSync(process.cwd() + '/dist/' + provider, 'utf8') : ''
|
|
1036
|
+
break;
|
|
1037
|
+
case provider === 'vercel.json':
|
|
1038
|
+
redirectFile = fs.existsSync(process.cwd() + '/' + provider) ? fs.readFileSync(process.cwd() + '/' + provider, 'utf8') : ''
|
|
1039
|
+
break;
|
|
1040
|
+
default:
|
|
1041
|
+
break;
|
|
1042
|
+
}
|
|
1043
|
+
let type = provider === '_redirects' ? 'text/plain' : 'application/json'
|
|
1044
|
+
|
|
1045
|
+
let root = obj.url.includes(':') ? obj.url.split('/:')[0] : obj.url
|
|
1046
|
+
switch (true) {
|
|
1082
1047
|
case root === '/':
|
|
1083
1048
|
break;
|
|
1084
1049
|
case type === 'text/plain' && !redirectFile.includes(obj.url) && obj.url.includes(':'):
|
|
1085
1050
|
let page = obj.pathname.split('/pages/')[1].replace('.jsx', '.js')
|
|
1086
1051
|
redirectFile += `\n/${page} /${page} 200\n${obj.url} ${root} 200\n`
|
|
1087
1052
|
!redirectFile.includes('/404') ? redirectFile += `\n/404 /404 404` : null
|
|
1088
|
-
fs.writeFileSync(process.cwd() + '/dist/' + provider, redirectFile)
|
|
1053
|
+
fs.writeFileSync(process.cwd() + '/dist/' + provider, redirectFile)
|
|
1089
1054
|
console.log(`Added ${obj.url} ${obj.url} 200 to ${provider}`)
|
|
1090
1055
|
break;
|
|
1091
|
-
case
|
|
1092
|
-
let json = JSON.parse(redirectFile)
|
|
1056
|
+
case type === 'application/json' && !redirectFile?.includes(`${obj.url}`):
|
|
1057
|
+
let json = redirectFile ? JSON.parse(redirectFile) : {}
|
|
1093
1058
|
let isVercel = provider === 'vercel.json' ? true : false
|
|
1094
|
-
if(isVercel){
|
|
1059
|
+
if (isVercel) {
|
|
1095
1060
|
json['rewrites'] = json['rewrites'] || []
|
|
1096
|
-
json['rewrites'].push({ "source":
|
|
1061
|
+
json['rewrites'].push({ "source": obj.url, "destination": `${root}/index.html` })
|
|
1062
|
+
fs.writeFileSync(process.cwd() + '/' + provider, JSON.stringify(json, null, 2))
|
|
1063
|
+
console.log(`Added ${obj.url} ${root}/index.html to ${provider}`)
|
|
1097
1064
|
}
|
|
1098
|
-
|
|
1099
|
-
console.log(`Added ${root}/* ${root} 200 to ${provider}`)
|
|
1065
|
+
|
|
1100
1066
|
}
|
|
1101
1067
|
}
|
|
1102
1068
|
break;
|
|
1103
1069
|
case config && config.host && config.host['_redirect']:
|
|
1104
|
-
let file =
|
|
1070
|
+
let file = config.host['_redirect']
|
|
1105
1071
|
file = file.split('./').join('')
|
|
1106
|
-
let redirectFile =
|
|
1072
|
+
let redirectFile = fs.existsSync(process.cwd() + '/' + file) ? fs.readFileSync(process.cwd() + '/' + file, 'utf8') : ''
|
|
1107
1073
|
fs.writeFileSync(process.cwd() + '/dist/' + file, redirectFile)
|
|
1108
1074
|
console.log(`Using ${file} for redirects`)
|
|
1109
1075
|
default:
|
|
1110
1076
|
break;
|
|
1111
1077
|
|
|
1112
1078
|
}
|
|
1113
|
-
|
|
1114
|
-
|
|
1079
|
+
|
|
1080
|
+
|
|
1115
1081
|
globalThis.routes.push({ fileName: fileName, url: obj.url, html: '/' + (isBasePath ? 'index.html' : `${obj.url}/` + 'index.html') })
|
|
1116
1082
|
|
|
1117
1083
|
|
|
@@ -1153,18 +1119,7 @@ async function Build() {
|
|
|
1153
1119
|
data = Compiler(data, process.cwd() + "/src/" + name);
|
|
1154
1120
|
|
|
1155
1121
|
await writer(process.cwd() + "/dist/src/" + name.split('.jsx').join('.js'), data).then(async () => {
|
|
1156
|
-
|
|
1157
|
-
try {
|
|
1158
|
-
let minified = await minify(data, {
|
|
1159
|
-
ecma: " 2016",
|
|
1160
|
-
module: true,
|
|
1161
|
-
compress: true,
|
|
1162
|
-
keep_fnames: true,
|
|
1163
|
-
})
|
|
1164
|
-
await writer(process.cwd() + "/dist/src/" + name.replace('.jsx', '.js'), minified.code)
|
|
1165
|
-
} catch (error) {
|
|
1166
|
-
console.log(error)
|
|
1167
|
-
}
|
|
1122
|
+
await writer(process.cwd() + "/dist/src/" + name.replace('.jsx', '.js'), data)
|
|
1168
1123
|
|
|
1169
1124
|
})
|
|
1170
1125
|
return
|
|
@@ -1217,6 +1172,7 @@ async function Build() {
|
|
|
1217
1172
|
console.log(`📦 Build completed: Build Size -> ${Math.round(bundleSize / 1000)}kb`)
|
|
1218
1173
|
|
|
1219
1174
|
bundleSize = 0;
|
|
1175
|
+
|
|
1220
1176
|
return true
|
|
1221
1177
|
}
|
|
1222
1178
|
const s = () => {
|
|
@@ -1236,18 +1192,17 @@ const s = () => {
|
|
|
1236
1192
|
|
|
1237
1193
|
fs.readFile(filePath, (err, data) => {
|
|
1238
1194
|
if (err) {
|
|
1239
|
-
console.log(err)
|
|
1240
1195
|
res.writeHead(404, { 'Content-Type': 'text/html' });
|
|
1241
1196
|
res.end(fs.existsSync(process.cwd() + '/dist/404') ? fs.readFileSync(process.cwd() + '/dist/404/index.html') : '404');
|
|
1242
1197
|
} else {
|
|
1243
|
-
const contentType = getContentType(filePath);
|
|
1198
|
+
const contentType = getContentType(filePath);
|
|
1244
1199
|
switch (true) {
|
|
1245
1200
|
case contentType === 'text/html' && globalThis.devMode:
|
|
1246
1201
|
data = data.toString() + `<script type="module">
|
|
1247
|
-
let ws = new WebSocket('ws://localhost
|
|
1202
|
+
let ws = new WebSocket('ws://localhost:${process.env.PORT || 3000}')
|
|
1248
1203
|
ws.onmessage = (e) => {
|
|
1249
1204
|
if(e.data === 'reload'){
|
|
1250
|
-
window.
|
|
1205
|
+
window.location.reload()
|
|
1251
1206
|
}
|
|
1252
1207
|
}
|
|
1253
1208
|
</script>
|
|
@@ -1262,13 +1217,13 @@ const s = () => {
|
|
|
1262
1217
|
|
|
1263
1218
|
const ws = new WebSocketServer({ server });
|
|
1264
1219
|
ws.on('connection', (socket) => {
|
|
1265
|
-
console.log('Client connected');
|
|
1266
|
-
socket.on('close', () => console.log('Client disconnected'));
|
|
1220
|
+
console.log('WebSocket Hydration Client connected');
|
|
1221
|
+
socket.on('close', () => console.log('WebSocket Hydration Client disconnected'));
|
|
1267
1222
|
});
|
|
1268
1223
|
|
|
1269
1224
|
|
|
1270
1225
|
function getContentType(filePath) {
|
|
1271
|
-
let ext =
|
|
1226
|
+
let ext = ['.js', '.css', '.mjs', '.cjs', '.html', '.json', '.png', '.jpg', '.jpeg', '.gif', '.svg', '.mp4', '.webm', '.ogg'].includes(path.extname(filePath)) ? path.extname(filePath) : '.html'
|
|
1272
1227
|
switch (ext) {
|
|
1273
1228
|
case '.js':
|
|
1274
1229
|
return 'text/javascript';
|
|
@@ -1310,21 +1265,20 @@ const s = () => {
|
|
|
1310
1265
|
let i =
|
|
1311
1266
|
setInterval(() => {
|
|
1312
1267
|
if (globalThis.isBuilding && globalThis.devMode) {
|
|
1313
|
-
|
|
1314
|
-
console.log(globalThis.isBuilding)
|
|
1315
|
-
console.log('Reloading page...')
|
|
1268
|
+
|
|
1316
1269
|
ws.clients.forEach((client) => {
|
|
1317
1270
|
client.send('reload')
|
|
1318
|
-
console.log('Reloaded page')
|
|
1319
1271
|
})
|
|
1320
|
-
}
|
|
1272
|
+
} else {
|
|
1273
|
+
clearInterval(i)
|
|
1274
|
+
}
|
|
1321
1275
|
}, 120)
|
|
1322
1276
|
|
|
1323
1277
|
}
|
|
1324
1278
|
|
|
1325
1279
|
|
|
1326
1280
|
switch (true) {
|
|
1327
|
-
case process.argv.includes('--watch'):
|
|
1281
|
+
case process.argv.includes('--watch') && !process.argv.includes('--build') && !process.argv.includes('--serve'):
|
|
1328
1282
|
|
|
1329
1283
|
globalThis.devMode = true
|
|
1330
1284
|
console.log(`
|
|
@@ -1348,13 +1302,15 @@ Vader.js v1.3.3
|
|
|
1348
1302
|
}
|
|
1349
1303
|
}).on('error', (err) => console.log(err))
|
|
1350
1304
|
})
|
|
1305
|
+
let p = process.argv[process.argv.indexOf('--watch') + 1] || process.env.PORT || 3000
|
|
1351
1306
|
|
|
1307
|
+
process.env.PORT = p
|
|
1352
1308
|
s()
|
|
1353
1309
|
|
|
1354
1310
|
globalThis.listen = true;
|
|
1355
1311
|
|
|
1356
1312
|
break;
|
|
1357
|
-
case process.argv.includes('--build'):
|
|
1313
|
+
case process.argv.includes('--build') && !process.argv.includes('--watch') && !process.argv.includes('--serve'):
|
|
1358
1314
|
globalThis.devMode = false
|
|
1359
1315
|
console.log(`
|
|
1360
1316
|
Vader.js v1.3.3
|
|
@@ -1363,7 +1319,7 @@ Building to ./dist
|
|
|
1363
1319
|
Build()
|
|
1364
1320
|
|
|
1365
1321
|
break;
|
|
1366
|
-
case process.argv.includes('--serve'):
|
|
1322
|
+
case process.argv.includes('--serve') && !process.argv.includes('--watch') && !process.argv.includes('--build'):
|
|
1367
1323
|
let port = process.argv[process.argv.indexOf('--serve') + 1] || 3000
|
|
1368
1324
|
process.env.PORT = port
|
|
1369
1325
|
globalThis.devMode = false
|
|
@@ -1377,19 +1333,19 @@ url: http://localhost:${port}
|
|
|
1377
1333
|
default:
|
|
1378
1334
|
console.log(`
|
|
1379
1335
|
Vader.js is a reactive framework for building interactive applications for the web built ontop of bun.js!
|
|
1380
|
-
|
|
1336
|
+
|
|
1381
1337
|
Usage: vader <command>
|
|
1382
|
-
|
|
1338
|
+
|
|
1383
1339
|
Commands:
|
|
1384
|
-
--watch
|
|
1385
|
-
|
|
1386
|
-
--build Build the project
|
|
1387
|
-
|
|
1388
|
-
--serve Serve the project on a port (default 3000)
|
|
1389
|
-
|
|
1390
|
-
Learn more about vader: https://vader-js.pages.dev/
|
|
1340
|
+
--watch (port) Watch the pages folder for changes with hot reloading
|
|
1391
1341
|
|
|
1392
|
-
|
|
1342
|
+
--build Build the project to ./dist
|
|
1343
|
+
|
|
1344
|
+
--serve (400) Serve the project on a port (default 3000 or process.env.PORT)
|
|
1345
|
+
|
|
1346
|
+
Learn more about vader: https://vader-js.pages.dev/
|
|
1347
|
+
|
|
1348
|
+
`)
|
|
1393
1349
|
break;
|
|
1394
1350
|
|
|
1395
1351
|
}
|