vaderjs 1.3.3-alpha-121 → 1.3.3-alpha-123

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "vaderjs",
3
3
  "description": "A Reactive library aimed to helping you build reactive applications inspired by react.js",
4
4
  "module": "vader.js",
5
- "version": "1.3.3-alpha-121",
5
+ "version": "1.3.3-alpha-123",
6
6
  "bin": {
7
7
  "vader": "./vader.js"
8
8
  },
package/runtime/router.js CHANGED
@@ -1 +1,445 @@
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]}`;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").innerHTML,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)},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 this.routes.find((t=>{if(t.path===e)return!0;if(""===e&&"/"===t.path)return!0;if(e.includes("?")&&(e=e.split("?")[0]),t.path.includes("*")||t.path.includes(":")){const r=t.path.split("/").filter((e=>""!==e)),n=e.split("/").filter((e=>""!==e));if(r.length!==n.length&&!t.path.endsWith("*"))return!1;for(let e=0;e<r.length;e++){const t=r[e],o=n[e];if(!t.startsWith(":")&&!t.startsWith("*")&&t!==o)return!1}return!0}const r=this.extractParams(t.path,e);return Object.keys(r).length>0}))}handleRoute(e){let t=200,r=e,n=this.checkroute(e);n||(n=window.routes.find((e=>!e.url.includes("/404")||this.error||window.devMode?!(this.error||!e.url.includes("/404"))||void 0:(window.history.pushState({},"","/404"),window.dispatchEvent(new Event("popstate")),this.error=!0,!1))),t=n?200:404);const o=this.extractQueryParams(r),s=n&&n.path?this.extractParams(n.path,r):{};Object.keys(s).forEach((e=>{s[e]=s[e].split("?")?s[e].split("?")[0]:s[e]}));const i={headers:{},params:s,query:o,path:e,fileUrl:window.location.href.split(window.location.origin)[1],url:window.location.href,method:n?n.method:"get",pause:!1,timestamp:Date.now()};window.$CURRENT_URL=i.path,window.$FULL_URL=window.location.href.replace("#","");const a={status:t,log:e=>{void 0===e?console.log(`${i.path} ${i.method} ${a.status} ${i.timestamp}`):console.table({"Request Path":i.path,"Request Method":n.method,"Response Status":a.status,"Request Timestamp":i.timestamp})},refresh:()=>{this.handleRoute(window.location.pathname)},redirect:e=>{!e.startsWith("/")&&(e=`/${e}`),window.history.pushState({},"",e),window.dispatchEvent(new Event("popstate"))},render:async(e,t,r,n)=>{function i(e){return"function"==typeof e&&/^class\s/.test(Function.prototype.toString.call(e))}try{let n=new Component;if(i(e.default)){let t=new e.default;n.state=t.state,n=t}else{if(e.default.toString().includes("this.key"))throw new Error('Using this.key is not supported in functional components use the attribute key="a value" instead');n.key=e.default.toString().split('key="')[1]?e.default.toString().split('key="')[1].split('"')[0]:null;let i={key:n.key,render:()=>e.default.apply(n,[t,r]),request:t,response:r,params:s,queryParams:o,reset:n.reset.bind(n),onMount:n.onMount.bind(n),useState:null,router:{use:n.router.use.bind(n)},bindMount:n.bindMount.bind(n),memoize:n.memoize.bind(n),createComponent:n.createComponent.bind(n),isChild:!1,useState:n.useState.bind(n),parseStyle:n.parseStyle.bind(n),bind:n.bind.bind(n),useRef:n.useRef.bind(n),useReducer:n.useReducer.bind(n),onMount:n.onMount.bind(n),onUnmount:n.onUnmount.bind(n),hydrate:n.hydrate.bind(n)};n.render=i.render,n=i}if(!document.querySelector("#root"))throw new Error("Root element not found, please add an element with id root");n.reset(),n.components={},n.request=t,n.response=r,n.router.use&&!n.isChild?await new Promise((async o=>{if(i(e.default))if(i(e.default))switch(await n.router.use(t,r),t.pause){case!0:console.log("pausing",t.pause);let e=setInterval((()=>{t.pause?console.log("still pausing",t.pause):(clearInterval(e),o())}),1e3);break;case!1:o()}else o();else switch(await e.default.apply(n,[t,r]),await n.router.use(t,r),t.pause){case!0:let e=setInterval((()=>{t.pause?console.log("still pausing request",t.url):(clearInterval(e),o())}),1e3);break;case!1:o()}})):n.router.use&&n.isChild&&console.warn("Router.use() is not supported in child components");const a=await n.render();document.querySelector("#root").innerHTML!==a&&(document.querySelector("#root").innerHTML=a),n.bindMount(),n.onMount()}catch(e){console.error(e)}},setQuery:e=>{let t="";Object.keys(e).forEach(((r,n)=>{t+=`${0===n?"?":"&"}${r}=${e[r]}`}));let r=window.location.hash.split("?")[0];t=t.replace("/","-").replaceAll("/","-"),window.location.hash=`${r}${t}`},send:e=>{document.querySelector("#root").innerHTML=e},json:e=>{const t=document.querySelector("#root");t.innerHTML="";const r=document.createElement("pre");r.textContent=JSON.stringify(e,null,2),t.appendChild(r)}};middlewares.forEach((e=>{e(i,a)})),n&&n.handler(i,a)}}window.VaderRouter=VaderRouter;export default VaderRouter;
1
+ import { Component } from "./vader.js";
2
+
3
+ let middlewares = [];
4
+
5
+
6
+
7
+ /**
8
+ * @class VaderRouter
9
+ * @description - creates an instance of Vader Express Router
10
+ *
11
+ * @param {String} path
12
+ * @param {Function} handler
13
+ * @param {object} req request object
14
+ * @param {object} res response object
15
+ * @returns {Object} Express
16
+ *
17
+ */
18
+ class VaderRouter{
19
+ /**
20
+ * @constructor
21
+ * @param {*} basePath
22
+ *
23
+ */
24
+ constructor(/**@type {string}**/basePath, /**@type {number}**/port) {
25
+ this.routes = [];
26
+ this.middlewares = [];
27
+ this.errorMiddlewares = [];
28
+ this.listeners = [];
29
+
30
+ this.basePath = basePath;
31
+
32
+
33
+ }
34
+
35
+ /**
36
+ * @method get
37
+ * @param {String} path
38
+ * @param {Function} handler
39
+ * @param {{a:b}} req request object
40
+ * @description This method is used to register a get route
41
+ * @returns {void}
42
+ * @memberof Express
43
+ */
44
+ get(path, handler) {
45
+ this.routes.push({
46
+ path,
47
+ handler,
48
+ method: 'get',
49
+ });
50
+
51
+ }
52
+ /**
53
+ * @method use
54
+ * @description This method allows you to use middlewares
55
+ * @param {Function} middleware
56
+ */
57
+
58
+ use(/* path, */ middleware) {
59
+ this.middlewares.push(middleware);
60
+ }
61
+
62
+ /**
63
+ * @method listen
64
+ * @param {String} port - unique id for the listener
65
+ * @param {Function} callback - callback function
66
+ * @description This method is used to start listening to the routes
67
+ * @returns {void}
68
+ *
69
+ */
70
+
71
+ listen(port, callback) {
72
+ if(!port){
73
+ port = Math.random().toString(36).substring(7);
74
+ }
75
+ window.onpopstate = async (e) => {
76
+ let route = window.location.pathname
77
+ let baseRoute = `/${route.split('/')[1]}`
78
+ if(!routes.find((route)=>route.url === baseRoute)){
79
+ console.error(`Route ${route} not found`);
80
+ }
81
+ let html = new DOMParser().parseFromString(await fetch(baseRoute, {
82
+ cache: 'reload'
83
+ }).then((res)=>res.text()), 'text/html').documentElement
84
+
85
+
86
+ document.querySelector('#root').innerHTML = html.querySelector('#root').innerHTML;
87
+ document.title = html.querySelector('title').innerHTML;
88
+ document.querySelector('script[id="router"]').remove();
89
+ let newscript = document.createElement('script');
90
+ newscript.id = 'router';
91
+ newscript.innerHTML = html.querySelector('script[id="router"]').innerHTML;
92
+ newscript.setAttribute('type', 'module');
93
+ document.body.appendChild(newscript);
94
+ }
95
+
96
+ this.listeners.push(port);
97
+ if (this.listeners.length === 1) {
98
+ this.handleRoute(window.location.pathname);
99
+ }else{
100
+ this.listeners.pop();
101
+ }
102
+ if (callback) {
103
+ callback();
104
+ }
105
+
106
+ }
107
+ /**
108
+ * @method extractParams
109
+ * @description This method is used to extract parameters from the route path
110
+ * @param {*} routePath
111
+ * @param {*} hash
112
+ * @returns {Object} params
113
+ * @memberof Express
114
+ */
115
+
116
+ extractParams(routePath, hash) {
117
+ const routeParts = routePath.split('/').filter((part) => part !== '');
118
+ const hashParts = hash.split('/').filter((part) => part !== '');
119
+ const params = {};
120
+ routeParts.forEach((part, index) => {
121
+ if (part.startsWith(':')) {
122
+ const paramName = part.slice(1);
123
+ params[paramName] = hashParts[index];
124
+ }else if(part.startsWith('*')){
125
+ let array = hashParts.slice(index)
126
+ array.forEach((i, index)=>{
127
+ params[index] = i
128
+ })
129
+ };
130
+ });
131
+ return params;
132
+ }
133
+ extractQueryParams(hash){
134
+
135
+ const queryParams = hash.split('?')[1];
136
+ if(!queryParams){
137
+ return {};
138
+ }
139
+ const params = {};
140
+ queryParams.split('&').forEach((param)=>{
141
+ const [key, value] = param.split('=');
142
+ params[key] = value;
143
+ })
144
+ return params;
145
+ }
146
+
147
+ checkroute(hash){
148
+ let route = this.routes.find((route) => {
149
+ if (route.path === hash) {
150
+ return true;
151
+ }
152
+
153
+ if(hash === '' && route.path === '/'){
154
+ return true
155
+ }
156
+
157
+ if(hash.includes('?')){
158
+ hash = hash.split('?')[0]
159
+ }
160
+ if (route.path.includes('*') || route.path.includes(':')) {
161
+ const routeParts = route.path.split('/').filter((part) => part !== '');
162
+ const hashParts = hash.split('/').filter((part) => part !== '');
163
+ if (routeParts.length !== hashParts.length && !route.path.endsWith('*')) {
164
+ return false;
165
+ }
166
+
167
+ for (let index = 0; index < routeParts.length; index++) {
168
+ const routePart = routeParts[index];
169
+ const hashPart = hashParts[index];
170
+
171
+
172
+ if (routePart.startsWith(':') || routePart.startsWith('*')) {
173
+
174
+ continue;
175
+ }
176
+
177
+ if (routePart !== hashPart) {
178
+ return false;
179
+ }
180
+ }
181
+
182
+ return true;
183
+ }
184
+ const params = this.extractParams(route.path, hash);
185
+ return Object.keys(params).length > 0;
186
+ });
187
+
188
+ return route;
189
+
190
+ }
191
+ /**
192
+ * @method handleRoute
193
+ * @param {String} hash
194
+ * @description This method is used to handle the route
195
+ */
196
+
197
+ handleRoute(hash) {
198
+ let status = 200;
199
+ let paramsCatchall = {}
200
+ let hashBefore = hash;
201
+
202
+ let route = this.checkroute(hash);
203
+ if (!route) {
204
+ route = window.routes.find((errorRoute) => {
205
+ if (errorRoute.url.includes('/404') && !this.error && !window.devMode) {
206
+ console.error(`Route ${hash} not found`);
207
+ this.error = true;
208
+ return false
209
+
210
+ } else if (!this.error && errorRoute.url.includes('/404')){
211
+ return true
212
+ }
213
+
214
+ });
215
+
216
+ status = route ? 200 : 404;
217
+ }
218
+
219
+ const queryParams = this.extractQueryParams(hashBefore);
220
+ const params = route && route.path ? this.extractParams(route.path, hashBefore) : paramsCatchall;
221
+
222
+
223
+ // remove queryparams fromparam
224
+ Object.keys(params).forEach((key)=>{
225
+ params[key] = params[key].split('?') ? params[key].split('?')[0] : params[key];
226
+ })
227
+ const req = {
228
+ headers: {},
229
+ params: params,
230
+ query: queryParams,
231
+ path: hash,
232
+ fileUrl: window.location.href.split(window.location.origin)[1],
233
+ url: window.location.href,
234
+ method: route ? route.method : 'get',
235
+ pause: false,
236
+ timestamp: Date.now(),
237
+ };
238
+
239
+ // @ts-ignore
240
+ window.$CURRENT_URL = req.path
241
+
242
+ // @ts-ignore
243
+ window.$FULL_URL = window.location.href.replace('#', '')
244
+
245
+ const res = {
246
+ status: status,
247
+ /**
248
+ * @method log
249
+ * @param {String} type
250
+ * @description This method is used to log the request and response
251
+ */
252
+ log: (type) => {
253
+ if(type === undefined){
254
+ console.log(`${req.path} ${req.method} ${res.status} ${req.timestamp}`);
255
+ }else{
256
+ console.table({
257
+ 'Request Path': req.path,
258
+ 'Request Method': route.method,
259
+ 'Response Status': res.status,
260
+ 'Request Timestamp': req.timestamp,
261
+ });
262
+ }
263
+ },
264
+ refresh: () => {
265
+ this.handleRoute(window.location.pathname)
266
+ },
267
+ redirect: (path) => {
268
+ !path.startsWith('/') ? path = `/${path}` : null;
269
+ window.history.pushState({}, '', path);
270
+ window.dispatchEvent(new Event('popstate'));
271
+ },
272
+ render: async (/**@type {Component} */ component, req, res, metadata) => {
273
+ function isClass(funcOrClass) {
274
+ return typeof funcOrClass === 'function' &&
275
+ /^class\s/.test(Function.prototype.toString.call(funcOrClass));
276
+ }
277
+
278
+ try {
279
+ let c = new Component();
280
+ if(!isClass(component.default)){
281
+ let render = component.default.toString();
282
+ if(render.includes('this.key')){
283
+ throw new Error('Using this.key is not supported in functional components use the attribute key="a value" instead')
284
+ }
285
+
286
+
287
+
288
+ c.key = component.default.toString().split('key="')[1] ? component.default.toString().split('key="')[1].split('"')[0] : null;
289
+
290
+ let comp = {
291
+ key: c.key,
292
+ render: () => {
293
+ return component.default.apply(c, [req, res])
294
+ },
295
+ request: req,
296
+ response: res,
297
+ params: params,
298
+ queryParams: queryParams,
299
+ reset: c.reset.bind(c),
300
+ onMount: c.onMount.bind(c),
301
+ useState: null,
302
+ router: {
303
+ use: c.router.use.bind(c),
304
+ },
305
+ bindMount: c.bindMount.bind(c),
306
+ memoize: c.memoize.bind(c),
307
+ createComponent: c.createComponent.bind(c),
308
+ isChild: false,
309
+ useState: c.useState.bind(c),
310
+ parseStyle: c.parseStyle.bind(c),
311
+ bind: c.bind.bind(c),
312
+ useRef: c.useRef.bind(c),
313
+ useReducer: c.useReducer.bind(c),
314
+ onMount: c.onMount.bind(c),
315
+ onUnmount: c.onUnmount.bind(c),
316
+ hydrate: c.hydrate.bind(c),
317
+ }
318
+ c.render = comp.render;
319
+ c = comp;
320
+
321
+ }else{
322
+ let comp = new component.default();
323
+ c.state = comp.state;
324
+ c = comp;
325
+ }
326
+
327
+
328
+
329
+
330
+
331
+
332
+
333
+
334
+
335
+ // Check if the root element exists
336
+ if (!document.querySelector('#root')) {
337
+ throw new Error('Root element not found, please add an element with id root');
338
+ }
339
+
340
+ c.reset();
341
+ c.components = {};
342
+ c.request = req;
343
+ c.response = res;
344
+ if (c.router.use && !c.isChild) {
345
+ await new Promise(async (resolve) => {
346
+ if(!isClass(component.default) ){
347
+ await component.default.apply(c, [req, res])
348
+ await c.router.use(req, res)
349
+ switch(req.pause){
350
+ case true:
351
+ let timer = setInterval(() => {
352
+ if (!req.pause) {
353
+ clearInterval(timer);
354
+ resolve();
355
+ }else{
356
+ console.log('still pausing request', req.url)
357
+ }
358
+ }, 1000);
359
+ break;
360
+ case false:
361
+ resolve();
362
+ break;
363
+ }
364
+ }else if(isClass(component.default)){
365
+
366
+ await c.router.use(req, res)
367
+ switch(req.pause){
368
+ case true:
369
+ console.log('pausing', req.pause)
370
+ let timer = setInterval(() => {
371
+ if (!req.pause) {
372
+ clearInterval(timer);
373
+ resolve();
374
+ }else{
375
+ console.log('still pausing', req.pause)
376
+ }
377
+ }, 1000);
378
+ break;
379
+ case false:
380
+ resolve();
381
+ break;
382
+ }
383
+ }else{
384
+ resolve();
385
+ }
386
+ });
387
+
388
+
389
+ } else if (c.router.use && c.isChild) {
390
+ console.warn('Router.use() is not supported in child components');
391
+ }
392
+ const renderedContent = await c.render();
393
+ if( document.querySelector('#root').innerHTML !== renderedContent){
394
+ document.querySelector('#root').innerHTML = renderedContent;
395
+ }
396
+ c.bindMount();
397
+ c.onMount();
398
+
399
+ } catch (error) {
400
+ console.error(error);
401
+ }
402
+ },
403
+ setQuery: (query) => {
404
+ let queryString = '';
405
+ Object.keys(query).forEach((key, index) => {
406
+ queryString += `${index === 0 ? '?' : '&'}${key}=${query[key]}`;
407
+ });
408
+ let route = window.location.hash.split('?')[0];
409
+ queryString = queryString.replace('/', '-').replaceAll('/', '-')
410
+ window.location.hash = `${route}${queryString}`;
411
+ },
412
+ send: (data) => {
413
+ document.querySelector('#root').innerHTML = data;
414
+ },
415
+ json: (data) => {
416
+ const rootElement = document.querySelector('#root');
417
+
418
+ // Clear existing content in #root
419
+ rootElement.innerHTML = '';
420
+
421
+ // Create a <pre> element
422
+ const preElement = document.createElement('pre');
423
+
424
+ // Set the text content of the <pre> element with formatted JSON
425
+ preElement.textContent = JSON.stringify(data, null, 2);
426
+
427
+ // Append the <pre> element to the #root element
428
+ rootElement.appendChild(preElement);
429
+ }
430
+
431
+ };
432
+ middlewares.forEach((middleware) => {
433
+ middleware(req, res);
434
+ });
435
+
436
+ route ? route.handler(req, res) : null;
437
+ }
438
+
439
+
440
+ }
441
+
442
+ window.VaderRouter = VaderRouter;
443
+
444
+ export default VaderRouter;
445
+
package/vader.js CHANGED
@@ -4,7 +4,11 @@ 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
+ import path from 'path'
9
+ let config = await import('file://' + process.cwd() + '/vader.config.js').then((e) => e.default || e)
10
+ console.log(config)
11
+
8
12
  let start = Date.now()
9
13
  let bundleSize = 0;
10
14
  let errorCodes = {
@@ -891,6 +895,7 @@ async function Build() {
891
895
  let origin = file.replace(/\\/g, '/');
892
896
  let fileName = origin.split('/pages/')[1].split('.jsx')[0].replace('.jsx', '') + '.jsx';
893
897
  let isBasePath = fileName === 'index.jsx';
898
+ let isParamRoute = fileName.includes('[') && fileName.includes(']') ? true : false
894
899
 
895
900
  // Extract all dynamic parameters from the file path [param1]/[param2]/[param3
896
901
  let aburl = origin.split('/pages')[1].split('.jsx')[0].replace('.jsx', '').split('[').join(':').split(']').join('');
@@ -941,9 +946,51 @@ async function Build() {
941
946
 
942
947
 
943
948
  obj.compiledPath = process.cwd() + "/dist/pages/" + fileName.replace('.jsx', '.js')
949
+ let providerRedirects = {cloudflare: '_redirect', vercel: 'vercel.json', netlify:'_redirects'}
950
+ switch(true){
951
+ case config && config.host && !config.host['_redirect']:
952
+ let host = config.host.provider
953
+
954
+ let provider = providerRedirects[host]
955
+ if(provider){
956
+
957
+ let redirectFile = fs.existsSync(process.cwd() + '/dist/' + provider) ? fs.readFileSync(process.cwd() + '/dist/' + provider, 'utf8') : ''
958
+ let type = provider === '_redirect' ? 'text/plain' : 'application/json'
959
+ let root = isParamRoute ? obj.url.split('/:')[0] : obj.url
960
+
961
+ switch(true){
962
+ case root === '/':
963
+ break;
964
+ case 'text/plain' && !redirectFile.includes(root):
965
+
966
+ redirectFile += `\n${root}/* ${root} 200`
967
+ fs.writeFileSync(process.cwd() + '/dist/' + provider, redirectFile)
968
+ console.log(`Added ${root}/* ${root} 200 to ${provider}`)
969
+ break;
970
+ case 'application/json' && !redirectFile.includes(root):
971
+ let json = JSON.parse(redirectFile) || {}
972
+ let isVercel = provider === 'vercel.json' ? true : false
973
+ if(isVercel){
974
+ json['rewrites'] = json['rewrites'] || []
975
+ json['rewrites'].push({ "source": `${root}/*`, "destination": `/${root}` })
976
+ }
977
+ fs.writeFileSync(process.cwd() + '/dist/' + provider, JSON.stringify(json))
978
+ console.log(`Added ${root}/* ${root} 200 to ${provider}`)
979
+ }
980
+ }
981
+ break;
982
+ case config && config.host && config.host['_redirect']:
983
+ let file = config.host['_redirect']
984
+ file = file.split('./').join('')
985
+ let redirectFile = fs.existsSync(process.cwd() + '/' + file) ? fs.readFileSync(process.cwd() + '/' + file, 'utf8') : ''
986
+ fs.writeFileSync(process.cwd() + '/dist/' + file, redirectFile)
987
+ console.log(`Using ${file} for redirects`)
988
+ default:
989
+ break;
944
990
 
945
-
946
-
991
+ }
992
+
993
+
947
994
  globalThis.routes.push({ fileName: fileName, url: obj.url, html: '/' + (isBasePath ? 'index.html' : `${obj.url}/` + 'index.html') })
948
995
 
949
996