vaderjs 1.3.3-alpha-45 → 1.3.3-alpha-47
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 +19 -2
- package/client/index.js +207 -326
- package/package.json +1 -1
- package/runtime/router.js +1 -333
- package/runtime/vader.js +1 -547
package/runtime/vader.js
CHANGED
|
@@ -1,547 +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
|
-
/**
|
|
11
|
-
* @method strictMount
|
|
12
|
-
* @description This method allows you to await until the component is mounted before running a callback
|
|
13
|
-
* @param {*} key
|
|
14
|
-
* @param {*} callback
|
|
15
|
-
*/
|
|
16
|
-
export const strictMount = (key, callback) => {
|
|
17
|
-
let timer = setInterval(() => {
|
|
18
|
-
if (mounts.find((m) => m.key === key)) {
|
|
19
|
-
clearInterval(timer);
|
|
20
|
-
callback();
|
|
21
|
-
}
|
|
22
|
-
}, 120);
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Represents a component in the Vader framework.
|
|
29
|
-
*/
|
|
30
|
-
export class Component {
|
|
31
|
-
/**
|
|
32
|
-
* Creates an instance of Component.
|
|
33
|
-
*/
|
|
34
|
-
constructor() {
|
|
35
|
-
this.state = {};
|
|
36
|
-
this.key = null;
|
|
37
|
-
this.components = {};
|
|
38
|
-
this.mounted = false;
|
|
39
|
-
this.checkIFMounted();
|
|
40
|
-
this.memoizes = []
|
|
41
|
-
this.functions = []
|
|
42
|
-
this.children = []
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Parent of the current component.
|
|
47
|
-
* @type {Component}
|
|
48
|
-
*/
|
|
49
|
-
this.parentNode = {}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Request object.
|
|
53
|
-
*/
|
|
54
|
-
this.request = {
|
|
55
|
-
/**
|
|
56
|
-
* @type {string}
|
|
57
|
-
* @description The headers for the current route
|
|
58
|
-
*/
|
|
59
|
-
headers:{},
|
|
60
|
-
/**
|
|
61
|
-
* @type {string}
|
|
62
|
-
* @description The method for the current route
|
|
63
|
-
*/
|
|
64
|
-
method: "GET",
|
|
65
|
-
/**
|
|
66
|
-
* @type {string}
|
|
67
|
-
* @description params for the given route /:id/:name etc
|
|
68
|
-
*/
|
|
69
|
-
params: {},
|
|
70
|
-
/**
|
|
71
|
-
* @type {string}
|
|
72
|
-
* @description path: current route path
|
|
73
|
-
*/
|
|
74
|
-
path: "",
|
|
75
|
-
/**
|
|
76
|
-
* @type {string}
|
|
77
|
-
* @description query: query object for the current route ?name=hello -> {name: 'hello'}
|
|
78
|
-
*/
|
|
79
|
-
query: {},
|
|
80
|
-
},
|
|
81
|
-
/**
|
|
82
|
-
* @type {string}
|
|
83
|
-
* @description The response object for the current route
|
|
84
|
-
*/
|
|
85
|
-
this.response = {
|
|
86
|
-
/**
|
|
87
|
-
* @method json
|
|
88
|
-
* @description This method allows you to send json data to the client
|
|
89
|
-
* @param {*} data
|
|
90
|
-
*/
|
|
91
|
-
json: (data) => {},
|
|
92
|
-
/**
|
|
93
|
-
* @method send
|
|
94
|
-
* @description This method allows you to send text data to the client
|
|
95
|
-
* @param {*} data
|
|
96
|
-
*/
|
|
97
|
-
send: (data) => {},
|
|
98
|
-
/**
|
|
99
|
-
* @method redirect
|
|
100
|
-
* @description This method allows you to redirect the client to a new route
|
|
101
|
-
* @param {*} path
|
|
102
|
-
*/
|
|
103
|
-
redirect: (path) => {},
|
|
104
|
-
/**
|
|
105
|
-
* @method render
|
|
106
|
-
* @description render a new component to the client
|
|
107
|
-
* @param {*} Component
|
|
108
|
-
*/
|
|
109
|
-
render: async (Component) => {},
|
|
110
|
-
/**
|
|
111
|
-
* @method log
|
|
112
|
-
* @description This method is used to log the request and response
|
|
113
|
-
* @param {String} type
|
|
114
|
-
*/
|
|
115
|
-
log: (type) => {},
|
|
116
|
-
/**
|
|
117
|
-
* @method setQuery
|
|
118
|
-
* @description This method is used to set the query object for the current route
|
|
119
|
-
*/
|
|
120
|
-
setQuery: (query) => {},
|
|
121
|
-
|
|
122
|
-
}
|
|
123
|
-
/**
|
|
124
|
-
* @method router
|
|
125
|
-
* @description use router methods directly from the parent component
|
|
126
|
-
*/
|
|
127
|
-
|
|
128
|
-
this.router = {
|
|
129
|
-
/**
|
|
130
|
-
* @method use
|
|
131
|
-
* @description add a middleware to the current route
|
|
132
|
-
* @param {Function} middleware
|
|
133
|
-
* @returns {void}
|
|
134
|
-
*/
|
|
135
|
-
use: (/**@type {Function} */ middleware) => {},
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
createComponent(/**@type {Component}**/component, props, children) {
|
|
140
|
-
|
|
141
|
-
if (!component) {
|
|
142
|
-
throw new Error("Component must be defined");
|
|
143
|
-
}
|
|
144
|
-
if(!props.key){
|
|
145
|
-
throw new Error('new components must have a key')
|
|
146
|
-
}
|
|
147
|
-
let comp = new component(props);
|
|
148
|
-
|
|
149
|
-
comp['props'] = props;
|
|
150
|
-
comp.children = children;
|
|
151
|
-
comp.props.children = children.join('')
|
|
152
|
-
comp.parentNode = this;
|
|
153
|
-
comp.request = this.request;
|
|
154
|
-
comp.response = this.response;
|
|
155
|
-
comp.key = props.key || null;
|
|
156
|
-
|
|
157
|
-
if(!this.components[props.key]){
|
|
158
|
-
this.components[props.key] = comp;
|
|
159
|
-
}
|
|
160
|
-
this.children.push(comp)
|
|
161
|
-
return this.components[props.key]
|
|
162
|
-
}
|
|
163
|
-
reset(){
|
|
164
|
-
console.log('reset')
|
|
165
|
-
Object.keys(this.components).forEach((key) => {
|
|
166
|
-
this.components[key].onUnmount()
|
|
167
|
-
delete this.components[key]
|
|
168
|
-
})
|
|
169
|
-
this.state = {}
|
|
170
|
-
this.children = []
|
|
171
|
-
}
|
|
172
|
-
memoize(/**@type {Component}**/component){
|
|
173
|
-
if(!component.key){
|
|
174
|
-
throw new Error('Component must have a static key')
|
|
175
|
-
}
|
|
176
|
-
switch(true){
|
|
177
|
-
case !this.memoizes.includes(component.key):
|
|
178
|
-
this.memoizes.push(component.key)
|
|
179
|
-
this.components[component.key] = component;
|
|
180
|
-
break;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
let comp = this.components[component.key];
|
|
184
|
-
comp.bindMount();
|
|
185
|
-
comp.parentNode = this;
|
|
186
|
-
comp.props = component.props;
|
|
187
|
-
comp.request = this.request;
|
|
188
|
-
comp.response = this.response;
|
|
189
|
-
let h = comp.render()
|
|
190
|
-
|
|
191
|
-
if(h && h.split('>,').length > 1){
|
|
192
|
-
h = h.replaceAll('>,', '>')
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
return `<div key="${component.key}">${h}</div>`
|
|
196
|
-
}
|
|
197
|
-
parseStyle(styles){
|
|
198
|
-
let css = ''
|
|
199
|
-
Object.keys(styles).forEach((key) => {
|
|
200
|
-
let value = styles[key]
|
|
201
|
-
key = key.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, '$1-$2').toLowerCase()
|
|
202
|
-
css += `${key}:${value};`
|
|
203
|
-
})
|
|
204
|
-
return css
|
|
205
|
-
}
|
|
206
|
-
bindMount(){
|
|
207
|
-
mounts.push(this)
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* Hydrates the component by updating the HTML content if it has changed.
|
|
212
|
-
* @private
|
|
213
|
-
*/
|
|
214
|
-
|
|
215
|
-
domDifference(oldDom, newDom) {
|
|
216
|
-
let diff = [];
|
|
217
|
-
|
|
218
|
-
for (let i = 0; i < oldDom.length; i++) {
|
|
219
|
-
let oldEl = oldDom[i];
|
|
220
|
-
let newEl = newDom[i];
|
|
221
|
-
|
|
222
|
-
if (oldEl && newEl && !oldEl.isEqualNode(newEl)) {
|
|
223
|
-
diff.push({
|
|
224
|
-
type: 'replace',
|
|
225
|
-
old: oldEl,
|
|
226
|
-
new: newEl.cloneNode(true),
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
return diff;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
updateChangedElements(diff) {
|
|
235
|
-
diff.forEach((change) => {
|
|
236
|
-
switch (change.type) {
|
|
237
|
-
case 'replace':
|
|
238
|
-
change.old.parentNode.replaceChild(change.new, change.old);
|
|
239
|
-
break;
|
|
240
|
-
case 'remove':
|
|
241
|
-
change.old.remove();
|
|
242
|
-
break;
|
|
243
|
-
case 'add':
|
|
244
|
-
change.old.appendChild(change.new.cloneNode(true));
|
|
245
|
-
break;
|
|
246
|
-
default:
|
|
247
|
-
break;
|
|
248
|
-
}
|
|
249
|
-
});
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
hydrate(hook) {
|
|
253
|
-
if (this.key) {
|
|
254
|
-
if (hook) {
|
|
255
|
-
let newDom = new DOMParser().parseFromString(this.render(), 'text/html').body.firstChild;
|
|
256
|
-
|
|
257
|
-
let oldElements = document.body.querySelectorAll(`[ref="${hook}"]`);
|
|
258
|
-
let newElements = newDom.querySelectorAll(`[ref="${hook}"]`);
|
|
259
|
-
let diff = this.domDifference(oldElements, newElements);
|
|
260
|
-
this.updateChangedElements(diff);
|
|
261
|
-
} else {
|
|
262
|
-
const targetElement = document.querySelector(`[key="${this.key}"]`);
|
|
263
|
-
if (targetElement) {
|
|
264
|
-
targetElement.outerHTML = this.render();
|
|
265
|
-
} else {
|
|
266
|
-
console.error('Target element not found.');
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
patch(oldElements, newElements) {
|
|
275
|
-
const diff = this.domDifference(oldElements, newElements);
|
|
276
|
-
|
|
277
|
-
this.updateChangedElements(diff);
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
/**
|
|
285
|
-
* Handles an object by parsing it as JSON and evaluating it.
|
|
286
|
-
* @param {string} obj - The object to handle.
|
|
287
|
-
* @returns {*} - The evaluated object.
|
|
288
|
-
* @prvate
|
|
289
|
-
*/
|
|
290
|
-
handleObject(obj) {
|
|
291
|
-
try {
|
|
292
|
-
obj = JSON.parse(obj);
|
|
293
|
-
} catch (error) {
|
|
294
|
-
// Handle JSON parsing error if needed
|
|
295
|
-
}
|
|
296
|
-
return eval(obj);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
/**
|
|
302
|
-
* Binds a function to the component.
|
|
303
|
-
* @param {string} funcData - The function data.
|
|
304
|
-
* @param {string} p - The parameter.
|
|
305
|
-
* @param {string} ref - The reference.
|
|
306
|
-
* @returns {string} - A valid inline JS function call.
|
|
307
|
-
*/
|
|
308
|
-
bind(funcTion,isTerny, jsx,ref, paramNames, ...params) {
|
|
309
|
-
ref = ref + this.key;
|
|
310
|
-
|
|
311
|
-
let paramObject = {};
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
paramNames = paramNames.replace(/,,/g, ',');
|
|
315
|
-
let newparamnames = paramNames.replaceAll(',,', ',')
|
|
316
|
-
|
|
317
|
-
for(var i in params){
|
|
318
|
-
let param = params[i]
|
|
319
|
-
let paramname = newparamnames.split(',')[i]
|
|
320
|
-
paramObject[paramname] = param
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
paramNames = paramNames.replace(',,', ',');
|
|
326
|
-
let func = new Function(`${paramNames}`, `
|
|
327
|
-
return (async (${paramNames}) => {
|
|
328
|
-
${funcTion.toString()}
|
|
329
|
-
})(${Object.keys(paramObject).join(',')})
|
|
330
|
-
`);
|
|
331
|
-
func = func.bind(this)
|
|
332
|
-
|
|
333
|
-
if(!this.functions.find((f) => f.ref === ref)){
|
|
334
|
-
document.addEventListener(`$dispatch_#id=${ref}`, (e) => {
|
|
335
|
-
let { name, event } = e.detail;
|
|
336
|
-
if (name === ref) {
|
|
337
|
-
let params = this.functions.find((f) => f.ref === ref).params
|
|
338
|
-
Object.keys(params).forEach((key) => {
|
|
339
|
-
if(params[key] instanceof CustomEvent){
|
|
340
|
-
delete params[key]
|
|
341
|
-
}
|
|
342
|
-
params[key] === undefined ? delete params[key] : params[key]
|
|
343
|
-
})
|
|
344
|
-
isTerny ? funcTion(event, ...Object.values(params)) : func(...Object.values(params))
|
|
345
|
-
}
|
|
346
|
-
});
|
|
347
|
-
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
window.callFunction = (name, event) => {
|
|
351
|
-
document.dispatchEvent(new CustomEvent(`$dispatch_#id=${name}`, { detail: { name: name, params: null, event: event } }));
|
|
352
|
-
}
|
|
353
|
-
!this.functions.find((f) => f.ref === ref) ? this.functions.push({ref: ref, params: paramObject}) : !isTerny ? this.functions.find((f) => f.ref === ref).params = paramObject : null
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
return jsx ? funcTion : `((event)=>{event.target.ev = event; callFunction('${ref}', event.target.ev)})(event)`;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
/**
|
|
362
|
-
* useState hook.
|
|
363
|
-
*
|
|
364
|
-
* @template T
|
|
365
|
-
* @param {string} key - The key for the state property.
|
|
366
|
-
* @param {T} initialState - The initial state value.
|
|
367
|
-
* @returns {[() => T, (newValue: T, hook: Function) => void]} - A tuple with getter and setter functions.
|
|
368
|
-
*/
|
|
369
|
-
useState(key, initialState) {
|
|
370
|
-
if (!this.state[key]) {
|
|
371
|
-
this.state[key] = initialState;
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
/**
|
|
375
|
-
* Get the current state value.
|
|
376
|
-
*
|
|
377
|
-
* @returns {T} The current state value.
|
|
378
|
-
*/
|
|
379
|
-
let updatedValue = () => this.state[key];
|
|
380
|
-
|
|
381
|
-
const getValue = updatedValue();
|
|
382
|
-
|
|
383
|
-
/**
|
|
384
|
-
* Set a new value for the state.
|
|
385
|
-
*
|
|
386
|
-
* @param {T} newValue - The new value to set.
|
|
387
|
-
* @param {Function} hook - The hook to hydrate after setting the value.
|
|
388
|
-
*/
|
|
389
|
-
const set = (newValue, hook) => {
|
|
390
|
-
this.state[key] = newValue;
|
|
391
|
-
this.hydrate(hook);
|
|
392
|
-
};
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
return [getValue, set];
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
useRef(key = null, initialState) {
|
|
402
|
-
if (!this.state[key]) {
|
|
403
|
-
this.state[key] = initialState;
|
|
404
|
-
}
|
|
405
|
-
const getValue = () => document.querySelector(`[ref="${key + this.key}"]`) || initialState;
|
|
406
|
-
const set = (newValue) => {
|
|
407
|
-
this.state[key] = newValue;
|
|
408
|
-
this.hydrate();
|
|
409
|
-
};
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
return {
|
|
413
|
-
bind: key + this.key,
|
|
414
|
-
current: getValue(),
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
useReducer(key = null, initialState, func = null) {
|
|
419
|
-
const getValue = () => this.state[key];
|
|
420
|
-
const value = getValue();
|
|
421
|
-
const set = (newValue) => {
|
|
422
|
-
const nextState = func ? func(this.state[key], newValue) : newValue;
|
|
423
|
-
this.state[key] = nextState;
|
|
424
|
-
};
|
|
425
|
-
return [value, set];
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
/**
|
|
430
|
-
* Placeholder for content to be rendered.
|
|
431
|
-
* @method render
|
|
432
|
-
*/
|
|
433
|
-
render() {}
|
|
434
|
-
|
|
435
|
-
/**
|
|
436
|
-
* Checks if the component is mounted and triggers the onMount method.
|
|
437
|
-
* @private
|
|
438
|
-
*/
|
|
439
|
-
checkIFMounted() {
|
|
440
|
-
let observer = new MutationObserver((mutations) => {
|
|
441
|
-
mutations.forEach((mutation) => {
|
|
442
|
-
|
|
443
|
-
if (mutation.target.querySelector(`[key="${this.key}"]`) && !this.mounted) {
|
|
444
|
-
this.onMount();
|
|
445
|
-
this.mounted = true;
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
if(Array.from(mutation.removedNodes).find((node) => node.attributes && node.attributes.key && node.attributes.key.value === this.key)){
|
|
449
|
-
this.onUnmount();
|
|
450
|
-
this.reset();
|
|
451
|
-
}
|
|
452
|
-
})
|
|
453
|
-
})
|
|
454
|
-
observer.observe(document.body, {
|
|
455
|
-
childList: true,
|
|
456
|
-
subtree: true,
|
|
457
|
-
});
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
/**
|
|
461
|
-
* Method that is called when the component is mounted.
|
|
462
|
-
* @method onMount
|
|
463
|
-
*/
|
|
464
|
-
onMount() {}
|
|
465
|
-
/**
|
|
466
|
-
* Method that is called when the component is unmounted.
|
|
467
|
-
* @method onUnmount
|
|
468
|
-
*/
|
|
469
|
-
onUnmount() {}
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
/**
|
|
475
|
-
* useState hook.
|
|
476
|
-
*
|
|
477
|
-
* @param {string} key - The key for the state property.
|
|
478
|
-
* @param {*} initialState - The initial state value.
|
|
479
|
-
* @returns {[*]} - A tuple with the current state value and a setter function.
|
|
480
|
-
*/
|
|
481
|
-
export const useState = (key, initialState) => {
|
|
482
|
-
if (!states[key]) {
|
|
483
|
-
states[key] = initialState;
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
/**
|
|
487
|
-
* Get the current state value.
|
|
488
|
-
*
|
|
489
|
-
* @returns {*} The current state value.
|
|
490
|
-
*/
|
|
491
|
-
let updatedValue = () => states[key];
|
|
492
|
-
|
|
493
|
-
/**
|
|
494
|
-
* Set a new value for the state.
|
|
495
|
-
*
|
|
496
|
-
* @param {*} newValue - The new value to set.
|
|
497
|
-
* @param {Function} hook - The hook to hydrate after setting the value.
|
|
498
|
-
*/
|
|
499
|
-
const set = (newValue, hook) => {
|
|
500
|
-
states[key] = newValue;
|
|
501
|
-
this.hydrate(hook);
|
|
502
|
-
};
|
|
503
|
-
|
|
504
|
-
return [states[key], set];
|
|
505
|
-
};
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
/**
|
|
510
|
-
* @method useReducer
|
|
511
|
-
* @param {*} initialState
|
|
512
|
-
* @param {*} reducer
|
|
513
|
-
* @returns {Array} [value, set]
|
|
514
|
-
*/
|
|
515
|
-
export const useReducer = (/**@type {*}**/initialState, /**@type {function}**/reducer) => {
|
|
516
|
-
return [initialState, (newValue) => {}];
|
|
517
|
-
};
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
/**
|
|
521
|
-
* useRef hook.
|
|
522
|
-
*
|
|
523
|
-
* @param {*} initialState - The initial state value for the ref.
|
|
524
|
-
* @returns {{ current: *, bind: string }} - An object containing the current value and a bind string.
|
|
525
|
-
*/
|
|
526
|
-
export const useRef = (initialState) => {
|
|
527
|
-
return {
|
|
528
|
-
/**
|
|
529
|
-
* @description The current value of the ref.
|
|
530
|
-
@type {*}
|
|
531
|
-
*/
|
|
532
|
-
current: initialState,
|
|
533
|
-
/**
|
|
534
|
-
* @description A unique string that can be used to bind the ref to an element.
|
|
535
|
-
* @type {HTMLElement|string}
|
|
536
|
-
*/
|
|
537
|
-
bind: '',
|
|
538
|
-
};
|
|
539
|
-
};
|
|
540
|
-
|
|
541
|
-
export default {
|
|
542
|
-
Component,
|
|
543
|
-
useRef,
|
|
544
|
-
useReducer,
|
|
545
|
-
useState,
|
|
546
|
-
strictMount,
|
|
547
|
-
}
|
|
1
|
+
window.Vader={version:"1.3.3"},window.componentRegistry={};let errors={"SyntaxError: Unexpected token '<'":"You forgot to enclose tags in a fragment <></>"},mounts=[];export const strictMount=(e,t)=>{let n=setInterval((()=>{mounts.find((t=>t.key===e))&&(clearInterval(n),t())}),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 o=s(e)?new e(t):null;if(!e)throw new Error("Component must be defined");if(!t.key)throw new Error("new components must have a key");if(s(e.default?e.default:e))o.props=t,o.children=n,o.props.children=n.join(""),o.parentNode=this,o.request=this.request,o.response=this.response,o.key=t.key||null;else{e.default&&(e=e.default);let s=new Component(t),r=e.toString(),i=r.includes("this.key")?r.split("this.key")[1].split("=")[1].split('"')[1]:null,u=(r.match(/return\s*`([\s\S]*)`/),r.split("return")[1].split("`")[0]);u=u.replace(/,\s*$/,""),t.children=n.join("");let a=e.apply(this,[t]);s.key=i,t.key=i,o={key:t.key?t.key:i,render:()=>a,request:this.request,response:this.response,reset:s.reset.bind(s),onMount:s.onMount.bind(s),parentNode:this,useState:s.useState.bind(s),useReducer:s.useReducer.bind(s),useRef:s.useRef.bind(s),router:{use:s.router.use.bind(s)},bindMount:s.bindMount.bind(s),memoize:s.memoize.bind(s),createComponent:s.createComponent.bind(s),isChild:!0,parseStyle:s.parseStyle.bind(s),components:{},onUnmount:s.onUnmount.bind(s),onMount:s.onMount.bind(s),functions:[],memoizes:[]}}return this.components[t.key]||(this.components[t.key]=o),this.children.push(o),this.components[t.key]}reset(){console.log("reset"),Object.keys(this.components).forEach((e=>{this.components[e].onUnmount(),delete this.components[e]})),this.state={},this.children=[]}memoize(e){if(!e.key)throw new Error("Component must have a static key");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;let n=t.render();return n&&n.split(">,").length>1&&(n=n.replaceAll(">,",">")),`<div key="${e.key}">${n}</div>`}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 o=e[s],r=t[s];o&&r&&!o.isEqualNode(r)&&n.push({type:"replace",old:o,new:r.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(this.key)if(e){let t=(new DOMParser).parseFromString(this.render(),"text/html").body.firstChild,n=document.body.querySelectorAll(`[ref="${e}"]`),s=t.querySelectorAll(`[ref="${e}"]`),o=this.domDifference(n,s);this.updateChangedElements(o)}else{const e=document.querySelector(`[key="${this.key}"]`);e?e.outerHTML=this.render():console.error("Target element not found.")}}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,o,...r){s+=this.key;let i={},u=(o=o.replace(/,,/g,",")).replaceAll(",,",",");for(var a in r){let e=r[a];i[u.split(",")[a]]=e}o=o.replace(",,",",");let d=new Function(`${o}`,`\n return (async (${o}) => { \n ${e.toString()}\n })(${Object.keys(i).join(",")}) \n `);return d=d.bind(this),this.functions.find((e=>e.ref===s))||document.addEventListener(`$dispatch_#id=${s}`,(n=>{let{name:o,event:r}=n.detail;if(o===s){let n=this.functions.find((e=>e.ref===s)).params;Object.keys(n).forEach((e=>{n[e]instanceof CustomEvent&&delete n[e],void 0===n[e]?delete n[e]:n[e]})),t?e(r,...Object.values(n)):d(...Object.values(n))}})),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))?!t&&(this.functions.find((e=>e.ref===s)).params=i):this.functions.push({ref:s,params:i}),n?e:`((event)=>{event.target.ev = event; callFunction('${s}', event.target.ev)})(event)`}useState(e,t){this.state[e]||(this.state[e]=t);return[(()=>this.state[e])(),(t,n)=>{this.state[e]=t,this.hydrate(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){return[(()=>this.state[e])(),t=>{const s=n?n(this.state[e],t):t;this.state[e]=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 default{Component:Component,useRef:useRef,useReducer:useReducer,useState:useState,strictMount:strictMount};
|