vaderjs 1.3.3-alpha-111 → 1.3.3-alpha-112

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-111",
5
+ "version": "1.3.3-alpha-112",
6
6
  "bin": {
7
7
  "vader": "./vader.js"
8
8
  },
package/runtime/router.js CHANGED
@@ -1,444 +1 @@
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
- console.log(this.basePath)
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
-
46
- this.routes.push({
47
- path,
48
- handler,
49
- method: 'get',
50
- });
51
-
52
- }
53
- /**
54
- * @method use
55
- * @description This method allows you to use middlewares
56
- * @param {Function} middleware
57
- */
58
-
59
- use(/* path, */ middleware) {
60
- this.middlewares.push(middleware);
61
- }
62
-
63
- /**
64
- * @method listen
65
- * @param {String} port - unique id for the listener
66
- * @param {Function} callback - callback function
67
- * @description This method is used to start listening to the routes
68
- * @returns {void}
69
- *
70
- */
71
-
72
- listen(port, callback) {
73
- if(!port){
74
- port = Math.random().toString(36).substring(7);
75
- }
76
- this.listeners.push(port);
77
- if (this.listeners.length === 1) {
78
- this.handleRoute(window.location.hash.length > 0 ? window.location.hash : window.location.pathname);
79
- }else{
80
- this.listeners.pop();
81
- }
82
- if (callback) {
83
- callback();
84
- }
85
- window.onpopstate = async (e) => {
86
- let route = window.location.pathname
87
- console.log(route)
88
- let baseRoute = `/${route.split('/')[1]}`
89
- if(!routes.find((route)=>route.url.includes(baseRoute))){
90
- baseRoute = '/404'
91
-
92
- }
93
- let html = new DOMParser().parseFromString(await fetch(baseRoute, {
94
- cache: 'reload'
95
- }).then((res)=>res.text()), 'text/html').documentElement
96
-
97
-
98
- document.querySelector('#root').innerHTML = html.querySelector('#root').innerHTML;
99
- document.title = html.querySelector('title').innerHTML;
100
- document.querySelector('script[id="router"]').remove();
101
- let newscript = document.createElement('script');
102
- newscript.id = 'router';
103
- newscript.innerHTML = html.querySelector('script[id="router"]').innerHTML;
104
- newscript.setAttribute('type', 'module');
105
- document.body.appendChild(newscript);
106
- }
107
- }
108
- /**
109
- * @method extractParams
110
- * @description This method is used to extract parameters from the route path
111
- * @param {*} routePath
112
- * @param {*} hash
113
- * @returns {Object} params
114
- * @memberof Express
115
- */
116
-
117
- extractParams(routePath, hash) {
118
- const routeParts = routePath.split('/');
119
- const hashParts = hash.split('/');
120
- const params = {};
121
- routeParts.forEach((part, index) => {
122
- if (part.startsWith(':')) {
123
- const paramName = part.slice(1);
124
- params[paramName] = hashParts[index];
125
- }else if(part.startsWith('*')){
126
- let array = hashParts.slice(index)
127
- array.forEach((i, index)=>{
128
- params[index] = i
129
- })
130
- }
131
- });
132
- return params;
133
- }
134
- extractQueryParams(hash){
135
-
136
- const queryParams = hash.split('?')[1];
137
- if(!queryParams){
138
- return {};
139
- }
140
- const params = {};
141
- queryParams.split('&').forEach((param)=>{
142
- const [key, value] = param.split('=');
143
- params[key] = value;
144
- })
145
- return params;
146
- }
147
-
148
- /**
149
- * @method handleRoute
150
- * @param {String} hash
151
- * @description This method is used to handle the route
152
- */
153
-
154
- handleRoute(hash) {
155
- hash = hash.includes('#') ? hash.slice(1) : hash;
156
- let status = 200;
157
- let paramsCatchall = {}
158
- let hashBefore = hash;
159
- let route = this.routes.find((route) => {
160
- if (route.path === hash) {
161
- return true;
162
- }
163
-
164
- if(hash === '' && route.path === '/'){
165
- return true
166
- }
167
-
168
- if(hash.includes('?')){
169
- hash = hash.split('?')[0]
170
- }
171
- if (route.path.includes('*') || route.path.includes(':')) {
172
- const routeParts = route.path.split('/');
173
- const hashParts = hash.split('/');
174
-
175
- if (routeParts.length !== hashParts.length && !route.path.endsWith('*')) {
176
- return false;
177
- }
178
-
179
- for (let index = 0; index < routeParts.length; index++) {
180
- const routePart = routeParts[index];
181
- const hashPart = hashParts[index];
182
-
183
- if (routePart.startsWith(':') || routePart.startsWith('*')) {
184
-
185
- continue;
186
- }
187
-
188
- if (routePart !== hashPart) {
189
-
190
- return false;
191
- }
192
- }
193
-
194
- return true;
195
- }
196
-
197
- const params = this.extractParams(route.path, hashBefore);
198
- return Object.keys(params).length > 0;
199
- });
200
-
201
-
202
- if (!route) {
203
- console.log(window.routes)
204
- route = window.routes.find((errorRoute) => {
205
- if (errorRoute.url.includes('/404')){
206
- window.history.replaceState({}, '', '/404');
207
-
208
- } else if (!this.error && errorRoute.url.includes('/404')){
209
- return true
210
- }
211
- });
212
-
213
- status = route ? 200 : 404;
214
- }
215
-
216
- const queryParams = this.extractQueryParams(hashBefore);
217
- const params = route && route.path ? this.extractParams(route.path, hashBefore) : {};
218
-
219
- // remove queryparams fromparam
220
- Object.keys(params).forEach((key)=>{
221
- params[key] = params[key].split('?') ? params[key].split('?')[0] : params[key];
222
- })
223
- const req = {
224
- headers: {},
225
- params: params,
226
- query: queryParams,
227
- path: hash,
228
- fileUrl: window.location.href.split(window.location.origin)[1],
229
- url: window.location.href,
230
- method: route ? route.method : 'get',
231
- pause: false,
232
- timestamp: Date.now(),
233
- };
234
-
235
- // @ts-ignore
236
- window.$CURRENT_URL = req.path
237
-
238
- // @ts-ignore
239
- window.$FULL_URL = window.location.href.replace('#', '')
240
-
241
- const res = {
242
- status: status,
243
- /**
244
- * @method log
245
- * @param {String} type
246
- * @description This method is used to log the request and response
247
- */
248
- log: (type) => {
249
- if(type === undefined){
250
- console.log(`${req.path} ${req.method} ${res.status} ${req.timestamp}`);
251
- }else{
252
- console.table({
253
- 'Request Path': req.path,
254
- 'Request Method': route.method,
255
- 'Response Status': res.status,
256
- 'Request Timestamp': req.timestamp,
257
- });
258
- }
259
- },
260
- refresh: () => {
261
- this.handleRoute(window.location.hash);
262
- },
263
- redirect: (path) => {
264
- !path.startsWith('/') ? path = `/${path}` : null;
265
- window.location.hash = `#${path}`;
266
- },
267
- render: async (/**@type {Component} */ component, req, res, metadata) => {
268
- function isClass(funcOrClass) {
269
- return typeof funcOrClass === 'function' &&
270
- /^class\s/.test(Function.prototype.toString.call(funcOrClass));
271
- }
272
-
273
- try {
274
- let c = new Component();
275
- if(!isClass(component.default)){
276
- let render = component.default.toString();
277
- if(render.includes('this.key')){
278
- throw new Error('Using this.key is not supported in functional components use the attribute key="a value" instead')
279
- }
280
-
281
-
282
-
283
- c.key = component.default.toString().split('key="')[1] ? component.default.toString().split('key="')[1].split('"')[0] : null;
284
-
285
- let comp = {
286
- key: c.key,
287
- render: () => {
288
- return component.default.apply(c, [req, res])
289
- },
290
- request: req,
291
- response: res,
292
- params: params,
293
- queryParams: queryParams,
294
- reset: c.reset.bind(c),
295
- onMount: c.onMount.bind(c),
296
- useState: null,
297
- router: {
298
- use: c.router.use.bind(c),
299
- },
300
- bindMount: c.bindMount.bind(c),
301
- memoize: c.memoize.bind(c),
302
- createComponent: c.createComponent.bind(c),
303
- isChild: false,
304
- useState: c.useState.bind(c),
305
- parseStyle: c.parseStyle.bind(c),
306
- bind: c.bind.bind(c),
307
- useRef: c.useRef.bind(c),
308
- useReducer: c.useReducer.bind(c),
309
- onMount: c.onMount.bind(c),
310
- onUnmount: c.onUnmount.bind(c),
311
- hydrate: c.hydrate.bind(c),
312
- }
313
- c.render = comp.render;
314
- c = comp;
315
-
316
-
317
-
318
- console.log(req)
319
- }else{
320
- let comp = new component.default();
321
- c.state = comp.state;
322
- c = comp;
323
- }
324
-
325
-
326
-
327
-
328
-
329
-
330
-
331
-
332
-
333
- // Check if the root element exists
334
- if (!document.querySelector('#root')) {
335
- throw new Error('Root element not found, please add an element with id root');
336
- }
337
-
338
- c.reset();
339
- c.components = {};
340
- c.request = req;
341
- c.response = res;
342
- if (c.router.use && !c.isChild) {
343
- await new Promise(async (resolve) => {
344
- if(!isClass(component.default) ){
345
- await component.default.apply(c, [req, res])
346
- await c.router.use(req, res)
347
- switch(req.pause){
348
- case true:
349
- let timer = setInterval(() => {
350
- if (!req.pause) {
351
- clearInterval(timer);
352
- resolve();
353
- }else{
354
- console.log('still pausing request', req.url)
355
- }
356
- }, 1000);
357
- break;
358
- case false:
359
- resolve();
360
- break;
361
- }
362
- }else if(isClass(component.default)){
363
-
364
- await c.router.use(req, res)
365
- switch(req.pause){
366
- case true:
367
- console.log('pausing', req.pause)
368
- let timer = setInterval(() => {
369
- if (!req.pause) {
370
- clearInterval(timer);
371
- resolve();
372
- }else{
373
- console.log('still pausing', req.pause)
374
- }
375
- }, 1000);
376
- break;
377
- case false:
378
- resolve();
379
- break;
380
- }
381
- }else{
382
- resolve();
383
- }
384
- });
385
-
386
-
387
- } else if (c.router.use && c.isChild) {
388
- console.warn('Router.use() is not supported in child components');
389
- }
390
- const renderedContent = await c.render();
391
- if( document.querySelector('#root').innerHTML !== renderedContent){
392
- console.log('rendering')
393
- document.querySelector('#root').innerHTML = renderedContent;
394
- }
395
- c.bindMount();
396
- c.onMount();
397
-
398
- } catch (error) {
399
- console.error(error);
400
- }
401
- },
402
- setQuery: (query) => {
403
- let queryString = '';
404
- Object.keys(query).forEach((key, index) => {
405
- queryString += `${index === 0 ? '?' : '&'}${key}=${query[key]}`;
406
- });
407
- let route = window.location.hash.split('?')[0];
408
- queryString = queryString.replace('/', '-').replaceAll('/', '-')
409
- window.location.hash = `${route}${queryString}`;
410
- },
411
- send: (data) => {
412
- document.querySelector('#root').innerHTML = data;
413
- },
414
- json: (data) => {
415
- const rootElement = document.querySelector('#root');
416
-
417
- // Clear existing content in #root
418
- rootElement.innerHTML = '';
419
-
420
- // Create a <pre> element
421
- const preElement = document.createElement('pre');
422
-
423
- // Set the text content of the <pre> element with formatted JSON
424
- preElement.textContent = JSON.stringify(data, null, 2);
425
-
426
- // Append the <pre> element to the #root element
427
- rootElement.appendChild(preElement);
428
- }
429
-
430
- };
431
- middlewares.forEach((middleware) => {
432
- middleware(req, res);
433
- });
434
-
435
- route ? route.handler(req, res) : null;
436
- }
437
-
438
-
439
- }
440
-
441
- window.VaderRouter = VaderRouter;
442
-
443
- export default VaderRouter;
444
-
1
+ import{Component}from"./vader.js";let middlewares=[];class VaderRouter{constructor(e,t){this.routes=[],this.middlewares=[],this.errorMiddlewares=[],this.listeners=[],this.basePath=e,console.log(this.basePath)}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)),this.listeners.push(e),1===this.listeners.length?this.handleRoute(window.location.pathname):this.listeners.pop(),t&&t(),window.onpopstate=async e=>{let t=window.location.pathname;console.log(t);let n=`/${t.split("/")[1]}`;routes.find((e=>e.url.includes(n)))||(n="/404");let s=(new DOMParser).parseFromString(await fetch(n,{cache:"reload"}).then((e=>e.text())),"text/html").documentElement;document.querySelector("#root").innerHTML=s.querySelector("#root").innerHTML,document.title=s.querySelector("title").innerHTML,document.querySelector('script[id="router"]').remove();let o=document.createElement("script");o.id="router",o.innerHTML=s.querySelector('script[id="router"]').innerHTML,o.setAttribute("type","module"),document.body.appendChild(o)}}extractParams(e,t){const n=e.split("/"),s=t.split("/"),o={};return n.forEach(((e,t)=>{if(e.startsWith(":")){const n=e.slice(1);o[n]=s[t]}else if(e.startsWith("*")){s.slice(t).forEach(((e,t)=>{o[t]=e}))}})),o}extractQueryParams(e){const t=e.split("?")[1];if(!t)return{};const n={};return t.split("&").forEach((e=>{const[t,s]=e.split("=");n[t]=s})),n}handleRoute(e){let t=200,n=e,s=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 n=t.path.split("/"),s=e.split("/");if(n.length!==s.length&&!t.path.endsWith("*"))return!1;for(let e=0;e<n.length;e++){const t=n[e],o=s[e];if(!t.startsWith(":")&&!t.startsWith("*")&&t!==o)return!1}return!0}const s=this.extractParams(t.path,n);return Object.keys(s).length>0}));s||(console.log(window.routes),s=window.routes.find((e=>{if(e.url.includes("/404"))window.history.replaceState({},"","/404");else if(!this.error&&e.url.includes("/404"))return!0})),t=s?200:404);const o=this.extractQueryParams(n),r=s&&s.path?this.extractParams(s.path,n):{};Object.keys(r).forEach((e=>{r[e]=r[e].split("?")?r[e].split("?")[0]:r[e]}));const i={headers:{},params:r,query:o,path:e,fileUrl:window.location.href.split(window.location.origin)[1],url:window.location.href,method:s?s.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":s.method,"Response Status":a.status,"Request Timestamp":i.timestamp})},refresh:()=>{this.handleRoute(window.location.hash)},redirect:e=>{!e.startsWith("/")&&(e=`/${e}`),window.location.hash=`#${e}`},render:async(e,t,n,s)=>{function isClass(e){return"function"==typeof e&&/^class\s/.test(Function.prototype.toString.call(e))}try{let s=new Component;if(isClass(e.default)){let t=new e.default;s.state=t.state,s=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');s.key=e.default.toString().split('key="')[1]?e.default.toString().split('key="')[1].split('"')[0]:null;let i={key:s.key,render:()=>e.default.apply(s,[t,n]),request:t,response:n,params:r,queryParams:o,reset:s.reset.bind(s),onMount:s.onMount.bind(s),useState:null,router:{use:s.router.use.bind(s)},bindMount:s.bindMount.bind(s),memoize:s.memoize.bind(s),createComponent:s.createComponent.bind(s),isChild:!1,useState:s.useState.bind(s),parseStyle:s.parseStyle.bind(s),bind:s.bind.bind(s),useRef:s.useRef.bind(s),useReducer:s.useReducer.bind(s),onMount:s.onMount.bind(s),onUnmount:s.onUnmount.bind(s),hydrate:s.hydrate.bind(s)};s.render=i.render,s=i,console.log(t)}if(!document.querySelector("#root"))throw new Error("Root element not found, please add an element with id root");s.reset(),s.components={},s.request=t,s.response=n,s.router.use&&!s.isChild?await new Promise((async o=>{if(isClass(e.default))if(isClass(e.default))switch(await s.router.use(t,n),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(s,[t,n]),await s.router.use(t,n),t.pause){case!0:let e=setInterval((()=>{t.pause?console.log("still pausing request",t.url):(clearInterval(e),o())}),1e3);break;case!1:o()}})):s.router.use&&s.isChild&&console.warn("Router.use() is not supported in child components");const i=await s.render();document.querySelector("#root").innerHTML!==i&&(console.log("rendering"),document.querySelector("#root").innerHTML=i),s.bindMount(),s.onMount()}catch(e){console.error(e)}},setQuery:e=>{let t="";Object.keys(e).forEach(((n,s)=>{t+=`${0===s?"?":"&"}${n}=${e[n]}`}));let n=window.location.hash.split("?")[0];t=t.replace("/","-").replaceAll("/","-"),window.location.hash=`${n}${t}`},send:e=>{document.querySelector("#root").innerHTML=e},json:e=>{const t=document.querySelector("#root");t.innerHTML="";const n=document.createElement("pre");n.textContent=JSON.stringify(e,null,2),t.appendChild(n)}};middlewares.forEach((e=>{e(i,a)})),s&&s.handler(i,a)}}window.VaderRouter=VaderRouter;export default VaderRouter;
package/runtime/vader.js CHANGED
@@ -1,656 +1 @@
1
- window.Vader = {
2
- version: "1.3.3",
3
- };
4
- window.componentRegistry = {};
5
- let errors = {
6
- "SyntaxError: Unexpected token '<'": "You forgot to enclose tags in a fragment <></>",
7
- }
8
-
9
- let mounts = [];
10
- let hasRan = []
11
- /**
12
- * @method strictMount
13
- * @description This method allows you to await until the component is mounted before running a callback
14
- * @param {*} key
15
- * @param {*} callback
16
- */
17
- export const strictMount = (key, callback) => {
18
- let timer = setInterval(() => {
19
- if (document.querySelector(`[key="${key}"]`) && !hasRan.includes(key)) {
20
- clearInterval(timer);
21
- callback();
22
- hasRan.push(key)
23
- }
24
- }, 120);
25
- };
26
-
27
-
28
-
29
- /**
30
- * Represents a component in the Vader framework.
31
- */
32
- export class Component {
33
- /**
34
- * Creates an instance of Component.
35
- */
36
- constructor() {
37
- this.state = {};
38
- this.key = null;
39
- this.components = {};
40
- this.mounted = false;
41
- this.checkIFMounted();
42
- this.memoizes = []
43
- this.functions = []
44
- this.children = []
45
-
46
-
47
- /**
48
- * Parent of the current component.
49
- * @type {Component}
50
- */
51
- this.parentNode = {}
52
-
53
- /**
54
- * Request object.
55
- */
56
- this.request = {
57
- /**
58
- * @type {string}
59
- * @description The headers for the current route
60
- */
61
- headers:{},
62
- /**
63
- * @type {string}
64
- * @description The method for the current route
65
- */
66
- method: "GET",
67
- /**
68
- * @type {string}
69
- * @description params for the given route /:id/:name etc
70
- */
71
- params: {},
72
- /**
73
- * @type {string}
74
- * @description path: current route path
75
- */
76
- path: "",
77
- /**
78
- * @type {string}
79
- * @description query: query object for the current route ?name=hello -> {name: 'hello'}
80
- */
81
- query: {},
82
- },
83
- /**
84
- * @type {string}
85
- * @description The response object for the current route
86
- */
87
- this.response = {
88
- /**
89
- * @method json
90
- * @description This method allows you to send json data to the client
91
- * @param {*} data
92
- */
93
- json: (data) => {},
94
- /**
95
- * @method send
96
- * @description This method allows you to send text data to the client
97
- * @param {*} data
98
- */
99
- send: (data) => {},
100
- /**
101
- * @method redirect
102
- * @description This method allows you to redirect the client to a new route
103
- * @param {*} path
104
- */
105
- redirect: (path) => {},
106
- /**
107
- * @method render
108
- * @description render a new component to the client
109
- * @param {*} Component
110
- */
111
- render: async (Component) => {},
112
- /**
113
- * @method log
114
- * @description This method is used to log the request and response
115
- * @param {String} type
116
- */
117
- log: (type) => {},
118
- /**
119
- * @method setQuery
120
- * @description This method is used to set the query object for the current route
121
- */
122
- setQuery: (query) => {},
123
-
124
- }
125
- /**
126
- * @method router
127
- * @description use router methods directly from the parent component
128
- */
129
-
130
- this.router = {
131
- /**
132
- * @method use
133
- * @description add a middleware to the current route
134
- * @param {Function} middleware
135
- * @returns {void}
136
- */
137
- use: (/**@type {Function} */ middleware) => {},
138
- }
139
- }
140
-
141
- createComponent(/**@type {Component}**/component, props, children) {
142
-
143
- function isClass(funcOrClass) {
144
- return typeof funcOrClass === 'function' &&
145
- /^class\s/.test(Function.prototype.toString.call(funcOrClass));
146
- }
147
- let comp = !isClass(component) ? null : new component(props);
148
- if (!component) {
149
- throw new Error("Component must be defined");
150
- }
151
- let c = new Component(props);
152
- if(!isClass(component)){
153
- let render = component.toString();
154
-
155
-
156
-
157
-
158
- c.key = component.toString().split('key="')[1] ? component.toString().split('key="')[1].split('"')[0] : null;
159
-
160
- let comp = {
161
- key: c.key,
162
- render: () => {
163
- return component.apply(c, [props])
164
- },
165
- request: this.request,
166
- isChild: true,
167
- response: this.response,
168
- params: this.request.params,
169
- queryParams: this.request.query,
170
- reset: c.reset.bind(c),
171
- onMount: c.onMount.bind(c),
172
- useState: null,
173
- router: {
174
- use: c.router.use.bind(c),
175
- },
176
- bindMount: c.bindMount.bind(c),
177
- memoize: c.memoize.bind(c),
178
- createComponent: c.createComponent.bind(c),
179
- isChild: false,
180
- useState: c.useState.bind(c),
181
- parseStyle: c.parseStyle.bind(c),
182
- bind: c.bind.bind(c),
183
- useRef: c.useRef.bind(c),
184
- request: this.request,
185
- response: this.response,
186
- useReducer: c.useReducer.bind(c),
187
- hydrate: c.hydrate.bind(c),
188
- onUnmount: c.onUnmount.bind(c),
189
- parentNoe: this,
190
- }
191
- c.render = comp.render;
192
- c = comp;
193
-
194
-
195
- } else{
196
- comp['props'] = props;
197
- comp.children = children;
198
- comp.props.children = children.join('')
199
- comp.parentNode = this;
200
- comp.request = this.request;
201
- comp.response = this.response;
202
- comp.key = props.key || null;
203
- c = comp;
204
- }
205
-
206
-
207
-
208
-
209
- if(!this.components[props.key]){
210
- this.components[props.key] = c
211
- }
212
- !this.children.includes(c) ? this.children.push(c) : null
213
- return this.components[props.key]
214
- }
215
- reset(){
216
- Object.keys(this.components).forEach((key) => {
217
- this.components[key].onUnmount()
218
- delete this.components[key]
219
- })
220
- this.state = {}
221
- this.children = []
222
- }
223
- memoize(/**@type {Component}**/component){
224
-
225
- switch(true){
226
- case !this.memoizes.includes(component.key):
227
- this.memoizes.push(component.key)
228
- this.components[component.key] = component;
229
- break;
230
- }
231
-
232
- let comp = this.components[component.key];
233
- comp.bindMount();
234
- comp.parentNode = this;
235
- comp.props = component.props;
236
- comp.request = this.request;
237
- comp.response = this.response;
238
- comp.onMount = component.onMount.bind(component);
239
- comp.onUnmount = component.onUnmount.bind(component);
240
- let h = comp.render()
241
-
242
- if(h && h.split('>,').length > 1){
243
- h = h.replaceAll('>,', '>')
244
- }
245
-
246
- return `<span key="${component.key}" >${h}</span>`
247
- }
248
- parseStyle(styles){
249
- let css = ''
250
- Object.keys(styles).forEach((key) => {
251
- let value = styles[key]
252
- key = key.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, '$1-$2').toLowerCase()
253
- css += `${key}:${value};`
254
- })
255
- return css
256
- }
257
- bindMount(){
258
- mounts.push(this)
259
- }
260
-
261
- /**
262
- * Hydrates the component by updating the HTML content if it has changed.
263
- * @private
264
- */
265
-
266
- /**
267
- * Hydrates the component by updating the HTML content if it has changed.
268
- * @private
269
- */
270
-
271
- domDifference(oldDom, newDom) {
272
- let diff = [];
273
-
274
- for (let i = 0; i < oldDom.length; i++) {
275
- let oldEl = oldDom[i];
276
- let newEl = newDom[i];
277
-
278
- if (oldEl && newEl && !oldEl.isEqualNode(newEl)) {
279
- diff.push({
280
- type: 'replace',
281
- old: oldEl,
282
- new: newEl.cloneNode(true),
283
- });
284
- }
285
- }
286
-
287
- return diff;
288
- }
289
-
290
- updateChangedElements(diff) {
291
- diff.forEach((change) => {
292
- switch (change.type) {
293
- case 'replace':
294
- change.old.parentNode.replaceChild(change.new, change.old);
295
- break;
296
- case 'remove':
297
- change.old.remove();
298
- break;
299
- case 'add':
300
- change.old.appendChild(change.new.cloneNode(true));
301
- break;
302
- default:
303
- break;
304
- }
305
- });
306
- }
307
- hydrate(hook) {
308
-
309
- if (hook) {
310
- let newDom = new DOMParser().parseFromString(this.render(), 'text/html').body.querySelector(`[ref="${hook}"]`);
311
- let oldDom = document.querySelector(`[ref="${hook}"]`);
312
-
313
- } else {
314
-
315
-
316
- let targetElement = this.key ? document.querySelector(`[key="${this.key}"]`) : null;
317
-
318
- let newDom = new DOMParser().parseFromString(this.render(), 'text/html').body
319
- newDom = document.createElement('div').appendChild(newDom)
320
-
321
-
322
- !targetElement ? targetElement = document.querySelector(`[key="${newDom.attributes?.key?.value || null}"]`) : null
323
- if(!targetElement){
324
- 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')
325
- return
326
- }
327
-
328
- newDom.querySelectorAll('*').forEach((el) => {
329
-
330
- if(el.hasAttribute('key') && el.innerHTML !== document.querySelector(`[key="${el.attributes.key.value}"]`).innerHTML){
331
-
332
- document.querySelector(`[key="${el.attributes.key.value}"]`).replaceWith(el)
333
- }
334
- });
335
-
336
-
337
-
338
- }
339
-
340
- }
341
-
342
-
343
-
344
- patch(oldElements, newElements) {
345
- const diff = this.domDifference(oldElements, newElements);
346
-
347
- this.updateChangedElements(diff);
348
- }
349
-
350
-
351
-
352
-
353
-
354
- /**
355
- * Handles an object by parsing it as JSON and evaluating it.
356
- * @param {string} obj - The object to handle.
357
- * @returns {*} - The evaluated object.
358
- * @prvate
359
- */
360
- handleObject(obj) {
361
- try {
362
- obj = JSON.parse(obj);
363
- } catch (error) {
364
- // Handle JSON parsing error if needed
365
- }
366
- return eval(obj);
367
- }
368
-
369
-
370
-
371
- /**
372
- * Binds a function to the component.
373
- * @param {string} funcData - The function data.
374
- * @param {string} p - The parameter.
375
- * @param {string} ref - The reference.
376
- * @returns {string} - A valid inline JS function call.
377
- */
378
- bind(funcTion, jsx,ref, paramNames, ...params) {
379
- ref = ref + this.key || 2022
380
-
381
- let paramObject = {};
382
-
383
-
384
- paramNames = paramNames.replace(/,,/g, ',');
385
- let newparamnames = paramNames.replaceAll(',,', ',')
386
-
387
- for(var i in params){
388
- let param = params[i]
389
- let paramname = newparamnames.split(',')[i]
390
- paramObject[paramname] = param
391
- }
392
-
393
-
394
-
395
- paramNames = paramNames.replace(',,', ',');
396
- let func = null
397
- // at the end of spaces or new lines add a ;
398
- funcTion = funcTion.split('\n').join(';')
399
- try{
400
- func = new Function(`event, ${paramNames}`, `
401
- return (async (event, ${paramNames}) => {
402
- ${funcTion.toString()}
403
- })(event, ${Object.keys(paramObject).join(',')})
404
- `);
405
- }catch(e){
406
- let { message } = e;
407
- console.error(`Error in function ${ref} ${message}`)
408
- }
409
- func = func.bind(this)
410
-
411
- if(!this.functions.find((f) => f.ref === ref)){
412
- document.addEventListener(`$dispatch_#id=${ref}`, (e) => {
413
- let { name, event } = e.detail;
414
- if (name === ref) {
415
- let params = this.functions.find((f) => f.ref === ref).params
416
- Object.keys(params).forEach((key) => {
417
- if(params[key] instanceof CustomEvent){
418
- delete params[key]
419
- }
420
-
421
- params[key] === undefined ? delete params[key] : params[key]
422
- })
423
- func(event, ...Object.values(params))
424
- }
425
- });
426
-
427
- }
428
-
429
- window.callFunction = (name, event) => {
430
- document.dispatchEvent(new CustomEvent(`$dispatch_#id=${name}`, { detail: { name: name, params: null, event: event } }));
431
- }
432
- !this.functions.find((f) => f.ref === ref) ? this.functions.push({ref: ref, params: paramObject}) : null
433
-
434
-
435
- return jsx ? funcTion : `((event)=>{event.target.ev = event; callFunction('${ref}', event.target.ev)})(event)`;
436
- }
437
-
438
-
439
-
440
- /**
441
- * useState hook.
442
- *
443
- * @template T
444
- * @param {string} key - The key for the state property.
445
- * @param {T} initialState - The initial state value.
446
- * @returns {[() => T, (newValue: T, hook: Function) => void]} - A tuple with getter and setter functions.
447
- */
448
- useState(key, initialState) {
449
- if (!this.state[key]) {
450
- this.state[key] = initialState;
451
- }
452
-
453
- /**
454
- * Get the current state value.
455
- *
456
- * @returns {T} The current state value.
457
- */
458
- let updatedValue = () => this.state[key];
459
-
460
- let getValue = updatedValue();
461
-
462
- /**
463
- * Set a new value for the state.
464
- *
465
- * @param {T} newValue - The new value to set.
466
- * @param {Function} hook - The hook to hydrate after setting the value.
467
- */
468
- const set = (newValue, hook) => {
469
- this.state[key] = newValue;
470
-
471
- this.hydrate(hook);
472
- getValue = updatedValue();
473
- };
474
-
475
-
476
-
477
- return [getValue, set];
478
- }
479
-
480
-
481
-
482
- useRef(key = null, initialState) {
483
- if (!this.state[key]) {
484
- this.state[key] = initialState;
485
- }
486
- const getValue = () => document.querySelector(`[ref="${key + this.key}"]`) || initialState;
487
- const set = (newValue) => {
488
- this.state[key] = newValue;
489
-
490
- this.hydrate();
491
- };
492
-
493
-
494
- return {
495
- bind: key + this.key,
496
- current: getValue(),
497
- }
498
- }
499
-
500
- useReducer(key = null, initialState, func = null) {
501
-
502
- if (!this.state[key]) {
503
- this.state[key] = initialState;
504
- }
505
- const getValue = () => this.state[key];
506
- let value = getValue();
507
- const set = (newValue, hook) => {
508
- const nextState = func(value, newValue) ?? newValue;
509
- this.state[key] = nextState;
510
- this.hydrate(hook);
511
- value = getValue();
512
- };
513
- return [getValue(), set];
514
- }
515
-
516
-
517
- /**
518
- * Placeholder for content to be rendered.
519
- * @method render
520
- */
521
- render() {}
522
-
523
- /**
524
- * Checks if the component is mounted and triggers the onMount method.
525
- * @private
526
- */
527
- checkIFMounted() {
528
- let observer = new MutationObserver((mutations) => {
529
- mutations.forEach((mutation) => {
530
-
531
- if (mutation.target.querySelector(`[key="${this.key}"]`) && !this.mounted) {
532
- this.onMount();
533
- this.mounted = true;
534
- }
535
-
536
- if(Array.from(mutation.removedNodes).find((node) => node.attributes && node.attributes.key && node.attributes.key.value === this.key)){
537
- this.onUnmount();
538
- this.reset();
539
- }
540
- })
541
- })
542
- observer.observe(document.body, {
543
- childList: true,
544
- subtree: true,
545
- });
546
- }
547
-
548
- /**
549
- * Method that is called when the component is mounted.
550
- * @method onMount
551
- */
552
- onMount() {}
553
- /**
554
- * Method that is called when the component is unmounted.
555
- * @method onUnmount
556
- */
557
- onUnmount() {}
558
- }
559
-
560
-
561
-
562
- /**
563
- * useState hook.
564
- *
565
- * @param {string} key - The key for the state property.
566
- * @param {*} initialState - The initial state value.
567
- * @returns {[*]} - A tuple with the current state value and a setter function.
568
- */
569
- export const useState = (key, initialState) => {
570
- if (!states[key]) {
571
- states[key] = initialState;
572
- }
573
-
574
- /**
575
- * Get the current state value.
576
- *
577
- * @returns {*} The current state value.
578
- */
579
- let updatedValue = () => states[key];
580
-
581
- /**
582
- * Set a new value for the state.
583
- *
584
- * @param {*} newValue - The new value to set.
585
- * @param {Function} hook - The hook to hydrate after setting the value.
586
- */
587
- const set = (newValue, hook) => {
588
- states[key] = newValue;
589
- this.hydrate(hook);
590
- };
591
-
592
- return [states[key], set];
593
- };
594
-
595
-
596
-
597
- /**
598
- * @method useReducer
599
- * @param {*} initialState
600
- * @param {*} reducer
601
- * @returns {Array} [value, set]
602
- */
603
- export const useReducer = (/**@type {*}**/initialState, /**@type {function}**/reducer) => {
604
- return [initialState, (newValue) => {}];
605
- };
606
-
607
-
608
- /**
609
- * useRef hook.
610
- *
611
- * @param {*} initialState - The initial state value for the ref.
612
- * @returns {{ current: *, bind: string }} - An object containing the current value and a bind string.
613
- */
614
- export const useRef = (initialState) => {
615
- return {
616
- /**
617
- * @description The current value of the ref.
618
- @type {*}
619
- */
620
- current: initialState,
621
- /**
622
- * @description A unique string that can be used to bind the ref to an element.
623
- * @type {HTMLElement|string}
624
- */
625
- bind: '',
626
- };
627
- };
628
-
629
- export class Link extends Component{
630
- constructor(props){
631
- super(props)
632
- this.props = props
633
- this.link = document.createElement('a')
634
- }
635
- render(){
636
-
637
-
638
- this.link.innerHTML = this.props.children
639
- this.link.setAttribute('id', this.props?.href)
640
- this.link.style = this.props?.style
641
-
642
- this.link.setAttribute('class', this.props?.class)
643
- this.link.setAttribute('onclick', `window.history.pushState({}, '', '${this.props?.href}'); window.dispatchEvent(new Event('popstate'));`)
644
- return this.link.outerHTML
645
- }
646
-
647
- }
648
-
649
- export default {
650
- Component,
651
- useRef,
652
- useReducer,
653
- useState,
654
- strictMount,
655
- Link
656
- }
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 n=setInterval((()=>{document.querySelector(`[key="${e}"]`)&&!hasRan.includes(e)&&(clearInterval(n),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,n){function s(e){return"function"==typeof e&&/^class\s/.test(Function.prototype.toString.call(e))}let r=s(e)?new e(t):null;if(!e)throw new Error("Component must be defined");let o=new Component(t);if(s(e))r.props=t,r.children=n,r.props.children=n.join(""),r.parentNode=this,r.request=this.request,r.response=this.response,r.key=t.key||null,o=r;else{e.toString();o.key=e.toString().split('key="')[1]?e.toString().split('key="')[1].split('"')[0]:null;let n={key:o.key,render:()=>e.apply(o,[t]),request:this.request,isChild:!0,response:this.response,params:this.request.params,queryParams:this.request.query,reset:o.reset.bind(o),onMount:o.onMount.bind(o),useState:null,router:{use:o.router.use.bind(o)},bindMount:o.bindMount.bind(o),memoize:o.memoize.bind(o),createComponent:o.createComponent.bind(o),isChild:!1,useState:o.useState.bind(o),parseStyle:o.parseStyle.bind(o),bind:o.bind.bind(o),useRef:o.useRef.bind(o),request:this.request,response:this.response,useReducer:o.useReducer.bind(o),hydrate:o.hydrate.bind(o),onUnmount:o.onUnmount.bind(o),parentNoe:this};o.render=n.render,o=n}return this.components[t.key]||(this.components[t.key]=o),!this.children.includes(o)&&this.children.push(o),this.components[t.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 n=t.render();return n&&n.split(">,").length>1&&(n=n.replaceAll(">,",">")),`<span key="${e.key}" >${n}</span>`}parseStyle(e){let t="";return Object.keys(e).forEach((n=>{let s=e[n];n=n.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g,"$1-$2").toLowerCase(),t+=`${n}:${s};`})),t}bindMount(){mounts.push(this)}domDifference(e,t){let n=[];for(let s=0;s<e.length;s++){let r=e[s],o=t[s];r&&o&&!r.isEqualNode(o)&&n.push({type:"replace",old:r,new:o.cloneNode(!0)})}return n}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 n=this.domDifference(e,t);this.updateChangedElements(n)}handleObject(obj){try{obj=JSON.parse(obj)}catch(e){}return eval(obj)}bind(e,t,n,s,...r){n=n+this.key||2022;let o={},i=(s=s.replace(/,,/g,",")).replaceAll(",,",",");for(var u in r){let e=r[u];o[i.split(",")[u]]=e}s=s.replace(",,",",");let a=null;e=e.split("\n").join(";");try{a=new Function(`event, ${s}`,` \n return (async (event, ${s}) => { \n ${e.toString()}\n })(event, ${Object.keys(o).join(",")}) \n `)}catch(e){let{message:t}=e;console.error(`Error in function ${n} ${t}`)}return a=a.bind(this),this.functions.find((e=>e.ref===n))||document.addEventListener(`$dispatch_#id=${n}`,(e=>{let{name:t,event:s}=e.detail;if(t===n){let e=this.functions.find((e=>e.ref===n)).params;Object.keys(e).forEach((t=>{e[t]instanceof CustomEvent&&delete e[t],void 0===e[t]?delete e[t]:e[t]})),a(s,...Object.values(e))}})),window.callFunction=(e,t)=>{document.dispatchEvent(new CustomEvent(`$dispatch_#id=${e}`,{detail:{name:e,params:null,event:t}}))},!this.functions.find((e=>e.ref===n))&&this.functions.push({ref:n,params:o}),t?e:`((event)=>{event.target.ev = event; callFunction('${n}', event.target.ev)})(event)`}useState(e,t){this.state[e]||(this.state[e]=t);let n=()=>this.state[e],s=n();return[s,(t,r)=>{this.state[e]=t,this.hydrate(r),s=n()}]}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,n=null){this.state[e]||(this.state[e]=t);const s=()=>this.state[e];let r=s();return[s(),(t,o)=>{const i=n(r,t)??t;this.state[e]=i,this.hydrate(o),r=s()}]}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,n)=>{states[e]=t,this.hydrate(n)}]};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")}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 default{Component:Component,useRef:useRef,useReducer:useReducer,useState:useState,strictMount:strictMount,Link:Link};