vaderjs 1.3.3-alpha-18 → 1.3.3-alpha-20
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 +24 -8
- package/client/index.js +231 -62
- package/package.json +1 -1
- package/runtime/index.html +1 -1
- package/runtime/router.js +3 -0
- package/runtime/vader.js +231 -62
- package/vader.js +116 -87
package/README.md
CHANGED
|
@@ -94,9 +94,10 @@ return {default:Home}
|
|
|
94
94
|
|
|
95
95
|
|
|
96
96
|
### State Management
|
|
97
|
+
Vaderjs uses partial hydration which is much better than react vdom, simply pass the ref key to state and only that element will update - no vdom required!!
|
|
97
98
|
|
|
98
99
|
```jsx
|
|
99
|
-
let {Component, useState} = await import('vaderjs/client')
|
|
100
|
+
let {Component, useState, useRef} = await import('vaderjs/client')
|
|
100
101
|
|
|
101
102
|
class MyApp extends Component{
|
|
102
103
|
contructor(){
|
|
@@ -106,12 +107,16 @@ class MyApp extends Component{
|
|
|
106
107
|
|
|
107
108
|
render(){
|
|
108
109
|
let [count, setCount] = useState(0)
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
110
|
+
let ref = useRef('')
|
|
111
|
+
|
|
112
112
|
return <>
|
|
113
|
-
<p>Count is ${count}</p>
|
|
114
|
-
|
|
113
|
+
<p ref={ref.bind}>Count is ${count}</p>
|
|
114
|
+
${/**
|
|
115
|
+
pass anything used from the toplevel render to the lowerlevel function params to be able to invoke!
|
|
116
|
+
**/}
|
|
117
|
+
<button onclick={(setCount, count, ref)=>{
|
|
118
|
+
setCount(count + 1, ref.bind)
|
|
119
|
+
}}>Increment</button>
|
|
115
120
|
</>
|
|
116
121
|
|
|
117
122
|
}
|
|
@@ -124,6 +129,7 @@ return {default:MyApp}
|
|
|
124
129
|
### Function Binding
|
|
125
130
|
|
|
126
131
|
Vaderjs allows you to bind functions directly to html elements just like react
|
|
132
|
+
there are two ways - top level invokes like below
|
|
127
133
|
|
|
128
134
|
```javascript
|
|
129
135
|
// vader uses params[0] as the event target object and other parameters resolve after
|
|
@@ -141,8 +147,18 @@ return <>
|
|
|
141
147
|
</>
|
|
142
148
|
```
|
|
143
149
|
|
|
144
|
-
|
|
145
|
-
|
|
150
|
+
and lower level invokes - these operate the same just allow you to pass items from top level to lower level: ex - I have a variable named car and i want the button to log it i can pass it as a parameter to allow it to be added to the buttons function scope
|
|
151
|
+
|
|
152
|
+
```jsx
|
|
153
|
+
let car = {
|
|
154
|
+
model: 'tesla',
|
|
155
|
+
price: 'toomiuch'
|
|
156
|
+
}
|
|
157
|
+
return <>
|
|
158
|
+
<button onclick={(car)=>{
|
|
159
|
+
console.log(car.model)
|
|
160
|
+
}}>Log</button>
|
|
161
|
+
```
|
|
146
162
|
|
|
147
163
|
|
|
148
164
|
|
package/client/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
window.params = {};
|
|
3
3
|
window.Vader = {
|
|
4
|
-
version: "1.3.
|
|
4
|
+
version: "1.3.3",
|
|
5
5
|
};
|
|
6
6
|
|
|
7
7
|
let errors = {
|
|
@@ -43,16 +43,13 @@ let hasran = [];
|
|
|
43
43
|
let states = {};
|
|
44
44
|
let mounts = [];
|
|
45
45
|
export const strictMount = (key, callback) => {
|
|
46
|
-
|
|
47
|
-
if(
|
|
48
|
-
|
|
49
|
-
|
|
46
|
+
let timer = setInterval(() => {
|
|
47
|
+
if (document.querySelector('[key="' + key + '"]')) {
|
|
48
|
+
console.log('mounted')
|
|
49
|
+
clearInterval(timer);
|
|
50
50
|
callback();
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
hasran.push(callback.toString());
|
|
54
|
-
}
|
|
55
|
-
},0);
|
|
51
|
+
}
|
|
52
|
+
}, 120);
|
|
56
53
|
};
|
|
57
54
|
|
|
58
55
|
window.delegate = (event) => {
|
|
@@ -167,9 +164,93 @@ export class Component {
|
|
|
167
164
|
window.listeners = [];
|
|
168
165
|
this.functionMap = new Map();
|
|
169
166
|
this.freeMemoryFromFunctions();
|
|
167
|
+
this.checkIFMounted();
|
|
170
168
|
this.memoizes = []
|
|
169
|
+
|
|
170
|
+
this.vdom = []
|
|
171
|
+
|
|
171
172
|
this.children = []
|
|
173
|
+
let dom = new DOMParser().parseFromString(this.render(), 'text/html').body.firstChild;
|
|
174
|
+
dom.querySelectorAll('*').forEach((el) => {
|
|
175
|
+
let obj = {
|
|
176
|
+
el: el,
|
|
177
|
+
tag: el.tagName,
|
|
178
|
+
html: el.innerHTML,
|
|
179
|
+
content: el.textContent,
|
|
180
|
+
value: el.value,
|
|
181
|
+
attributes: el.attributes,
|
|
182
|
+
children: el.childNodes,
|
|
183
|
+
}
|
|
184
|
+
this.vdom.push(obj)
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Parent of the current component.
|
|
189
|
+
* @type {Component}
|
|
190
|
+
*/
|
|
191
|
+
this.parentNode = {}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Request object.
|
|
195
|
+
*/
|
|
196
|
+
this.request = {
|
|
197
|
+
/**
|
|
198
|
+
* @type {string}
|
|
199
|
+
* @description The headers for the current route
|
|
200
|
+
*/
|
|
201
|
+
headers:{},
|
|
202
|
+
/**
|
|
203
|
+
* @type {string}
|
|
204
|
+
* @description The method for the current route
|
|
205
|
+
*/
|
|
206
|
+
method: "GET",
|
|
207
|
+
/**
|
|
208
|
+
* @type {string}
|
|
209
|
+
* @description params for the given route /:id/:name etc
|
|
210
|
+
*/
|
|
211
|
+
params: {},
|
|
212
|
+
/**
|
|
213
|
+
* @type {string}
|
|
214
|
+
* @description path: current route path
|
|
215
|
+
*/
|
|
216
|
+
path: "",
|
|
217
|
+
/**
|
|
218
|
+
* @type {string}
|
|
219
|
+
* @description query: query object for the current route ?name=hello -> {name: 'hello'}
|
|
220
|
+
*/
|
|
221
|
+
query: {},
|
|
222
|
+
},
|
|
223
|
+
/**
|
|
224
|
+
* @type {string}
|
|
225
|
+
* @description The response object for the current route
|
|
226
|
+
*/
|
|
227
|
+
this.response = {
|
|
228
|
+
/**
|
|
229
|
+
* @method json
|
|
230
|
+
* @description This method allows you to send json data to the client
|
|
231
|
+
* @param {*} data
|
|
232
|
+
*/
|
|
233
|
+
json: (data) => {},
|
|
234
|
+
/**
|
|
235
|
+
* @method send
|
|
236
|
+
* @description This method allows you to send text data to the client
|
|
237
|
+
* @param {*} data
|
|
238
|
+
*/
|
|
239
|
+
send: (data) => {},
|
|
240
|
+
/**
|
|
241
|
+
* @method redirect
|
|
242
|
+
* @description This method allows you to redirect the client to a new route
|
|
243
|
+
* @param {*} path
|
|
244
|
+
*/
|
|
245
|
+
redirect: (path) => {},
|
|
246
|
+
/**
|
|
247
|
+
* @method render
|
|
248
|
+
* @description render a new component to the client
|
|
249
|
+
* @param {*} Component
|
|
250
|
+
*/
|
|
251
|
+
render: async (Component) => {},
|
|
172
252
|
}
|
|
253
|
+
}
|
|
173
254
|
|
|
174
255
|
createComponent(/**@type {Component}**/component, props, children) {
|
|
175
256
|
|
|
@@ -179,26 +260,16 @@ export class Component {
|
|
|
179
260
|
if(!props.key){
|
|
180
261
|
throw new Error('new components must have a key')
|
|
181
262
|
}
|
|
182
|
-
let comp = new component();
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
if (newFunc) {
|
|
187
|
-
newFunc = newFunc[0].replace(/this.bind\((.*)\)/gs, "$1");
|
|
188
|
-
newFunc = newFunc.split('`')[1]
|
|
189
|
-
props[key] = this.bind(newFunc, { jsx: true, params: props[key], ref: props.ref });
|
|
190
|
-
return
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
props[key] = props[key].toString().replace('"', '').replace("'", '').replace('${', '').replace('}', '')
|
|
196
|
-
});
|
|
263
|
+
let comp = new component(props);
|
|
264
|
+
if(this.components[props.key]){
|
|
265
|
+
return this.components[props.key]
|
|
266
|
+
}
|
|
197
267
|
comp['props'] = props;
|
|
198
268
|
comp.children = children;
|
|
199
269
|
comp.props.children = children.join('')
|
|
200
270
|
comp.parentNode = this;
|
|
201
271
|
comp.key = props.key || null;
|
|
272
|
+
|
|
202
273
|
this.components[props.key] = comp
|
|
203
274
|
this.children.push(comp)
|
|
204
275
|
return this.components[props.key]
|
|
@@ -215,6 +286,7 @@ export class Component {
|
|
|
215
286
|
}
|
|
216
287
|
|
|
217
288
|
let comp = this.components[component.key];
|
|
289
|
+
|
|
218
290
|
let h = comp.render()
|
|
219
291
|
|
|
220
292
|
if(h && h.split('>,').length > 1){
|
|
@@ -240,28 +312,75 @@ export class Component {
|
|
|
240
312
|
* Hydrates the component by updating the HTML content if it has changed.
|
|
241
313
|
* @private
|
|
242
314
|
*/
|
|
243
|
-
hydrate() {
|
|
244
|
-
if (this.key) {
|
|
245
|
-
|
|
246
|
-
const el = document.querySelector(`[key="${this.key}"]`);
|
|
247
315
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
316
|
+
domDifference(oldDom, newDom) {
|
|
317
|
+
let diff = [];
|
|
318
|
+
|
|
319
|
+
for (let i = 0; i < oldDom.length; i++) {
|
|
320
|
+
let oldEl = oldDom[i];
|
|
321
|
+
let newEl = newDom[i];
|
|
322
|
+
|
|
323
|
+
if (oldEl && newEl && !oldEl.isEqualNode(newEl)) {
|
|
324
|
+
diff.push({
|
|
325
|
+
type: 'replace',
|
|
326
|
+
old: oldEl,
|
|
327
|
+
new: newEl.cloneNode(true),
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return diff;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
updateChangedElements(diff) {
|
|
336
|
+
diff.forEach((change) => {
|
|
337
|
+
switch (change.type) {
|
|
338
|
+
case 'replace':
|
|
339
|
+
change.old.parentNode.replaceChild(change.new, change.old);
|
|
340
|
+
break;
|
|
341
|
+
case 'remove':
|
|
342
|
+
change.old.remove();
|
|
343
|
+
break;
|
|
344
|
+
case 'add':
|
|
345
|
+
change.old.appendChild(change.new.cloneNode(true));
|
|
346
|
+
break;
|
|
347
|
+
default:
|
|
348
|
+
break;
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
hydrate(hook) {
|
|
354
|
+
if (this.key) {
|
|
355
|
+
if (hook) {
|
|
356
|
+
let newDom = new DOMParser().parseFromString(this.render(), 'text/html').body.firstChild;
|
|
357
|
+
|
|
358
|
+
let oldElements = document.body.querySelectorAll(`[ref="${hook}"]`);
|
|
359
|
+
let newElements = newDom.querySelectorAll(`[ref="${hook}"]`);
|
|
360
|
+
let diff = this.domDifference(oldElements, newElements);
|
|
361
|
+
this.updateChangedElements(diff);
|
|
362
|
+
} else {
|
|
363
|
+
const targetElement = document.querySelector(`[key="${this.key}"]`);
|
|
364
|
+
if (targetElement) {
|
|
365
|
+
targetElement.innerHTML = this.render();
|
|
366
|
+
} else {
|
|
367
|
+
console.error('Target element not found.');
|
|
261
368
|
}
|
|
262
369
|
}
|
|
263
370
|
}
|
|
264
371
|
}
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
patch(oldElements, newElements) {
|
|
376
|
+
const diff = this.domDifference(oldElements, newElements);
|
|
377
|
+
|
|
378
|
+
this.updateChangedElements(diff);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
|
|
265
384
|
|
|
266
385
|
/**
|
|
267
386
|
* Handles an object by parsing it as JSON and evaluating it.
|
|
@@ -279,7 +398,7 @@ export class Component {
|
|
|
279
398
|
}
|
|
280
399
|
|
|
281
400
|
/**
|
|
282
|
-
* Frees memory from functions that have not been used for a certain period of time.
|
|
401
|
+
* Frees memory from functions that have not been used for a certain period of time.c
|
|
283
402
|
* @private
|
|
284
403
|
*/
|
|
285
404
|
freeMemoryFromFunctions() {
|
|
@@ -299,23 +418,47 @@ export class Component {
|
|
|
299
418
|
* @param {string} ref - The reference.
|
|
300
419
|
* @returns {string} - A valid inline JS function call.
|
|
301
420
|
*/
|
|
302
|
-
bind(funcData,
|
|
421
|
+
bind(funcData,jsx,ref, paramNames, ...params) {
|
|
422
|
+
|
|
303
423
|
|
|
304
424
|
const name = `func_${crypto ? crypto.getRandomValues(new Uint32Array(1))[0] : Math.random()}`;
|
|
305
425
|
|
|
306
|
-
var dynamicFunction = (params) => {
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
426
|
+
var dynamicFunction = async (...params) => {
|
|
427
|
+
|
|
428
|
+
// Replace double commas with a single comma
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
// Turn params into a destructured object
|
|
432
|
+
let paramObject = {};
|
|
433
|
+
params = params[0]
|
|
434
|
+
|
|
435
|
+
paramNames = paramNames.replace(/,,/g, ',');
|
|
436
|
+
let newparamnames = paramNames.replaceAll(',,', ',')
|
|
437
|
+
// Remove trailing commas
|
|
438
|
+
paramNames.endsWith(',') ? paramNames = paramNames.slice(0, -1) : null;
|
|
439
|
+
console.log(paramNames)
|
|
440
|
+
|
|
441
|
+
params.forEach((param, index) => {
|
|
442
|
+
paramObject[newparamnames.split(',')[index]] = param;
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
paramNames = paramNames.replace(',,', ',');
|
|
446
|
+
let func = new Function(`${paramNames}`, `
|
|
447
|
+
return (async (${paramNames}) => {
|
|
448
|
+
${funcData}
|
|
449
|
+
})(${Object.keys(paramObject).join(',')})
|
|
450
|
+
`);
|
|
451
|
+
|
|
452
|
+
// Bind and execute the function with the provided parameters
|
|
453
|
+
await func.bind(this)(...Object.values(paramObject));
|
|
312
454
|
};
|
|
455
|
+
|
|
313
456
|
|
|
314
457
|
dynamicFunction = dynamicFunction.bind(this);
|
|
315
458
|
if (!this.functionMap.has(name)) {
|
|
316
459
|
document.addEventListener(`call_${name}`, (e) => {
|
|
317
460
|
|
|
318
|
-
dynamicFunction();
|
|
461
|
+
dynamicFunction(params);
|
|
319
462
|
this.functionMap.set(e.detail.name, {
|
|
320
463
|
lastUsed: Date.now(),
|
|
321
464
|
});
|
|
@@ -335,15 +478,17 @@ export class Component {
|
|
|
335
478
|
};
|
|
336
479
|
|
|
337
480
|
// Return a valid inline js function call
|
|
338
|
-
return
|
|
481
|
+
return jsx ? dynamicFunction : `
|
|
339
482
|
((event) => {
|
|
340
|
-
event.target.setAttribute('data-ref', '${
|
|
483
|
+
event.target.setAttribute('data-ref', '${ref}');
|
|
341
484
|
let reference = event.target.getAttribute('data-ref');
|
|
342
485
|
event.target.eventData = event;
|
|
486
|
+
|
|
343
487
|
let domquery = queryRef(reference);
|
|
344
|
-
|
|
345
|
-
domquery.eventData
|
|
346
|
-
|
|
488
|
+
|
|
489
|
+
domquery.eventData = event;
|
|
490
|
+
domquery.eventData.target = domquery;
|
|
491
|
+
call('${name}', {event:domquery.eventData}, '${paramNames}')
|
|
347
492
|
})(event)
|
|
348
493
|
`;
|
|
349
494
|
}
|
|
@@ -412,11 +557,14 @@ export class Component {
|
|
|
412
557
|
if (!this.state[key]) {
|
|
413
558
|
this.state[key] = initialState;
|
|
414
559
|
}
|
|
415
|
-
|
|
416
|
-
const
|
|
560
|
+
let updatedValue = () => this.state[key];
|
|
561
|
+
const getValue = updatedValue()
|
|
562
|
+
const set = (newValue, hook) => {
|
|
417
563
|
this.state[key] = newValue;
|
|
418
|
-
this.hydrate();
|
|
564
|
+
this.hydrate(hook);
|
|
419
565
|
};
|
|
566
|
+
this[`set${key}`] = set;
|
|
567
|
+
this[`get${key}`] = getValue;
|
|
420
568
|
return [getValue, set];
|
|
421
569
|
}
|
|
422
570
|
|
|
@@ -429,9 +577,11 @@ export class Component {
|
|
|
429
577
|
this.state[key] = newValue;
|
|
430
578
|
this.hydrate();
|
|
431
579
|
};
|
|
580
|
+
|
|
581
|
+
|
|
432
582
|
return {
|
|
433
|
-
bind: key,
|
|
434
|
-
current:
|
|
583
|
+
bind: key + this.key,
|
|
584
|
+
current: document.querySelector(`[ref="${key + this.key}"]`) || initialState
|
|
435
585
|
}
|
|
436
586
|
}
|
|
437
587
|
|
|
@@ -455,7 +605,7 @@ export class Component {
|
|
|
455
605
|
* @private
|
|
456
606
|
*/
|
|
457
607
|
checkIFMounted() {
|
|
458
|
-
if (this.mounted) return;
|
|
608
|
+
if (this.mounted || !this.key) return;
|
|
459
609
|
let timer = setInterval(() => {
|
|
460
610
|
if (document.querySelector('[key="' + this.key + '"]')) {
|
|
461
611
|
clearInterval(timer);
|
|
@@ -533,6 +683,7 @@ window.require = require;
|
|
|
533
683
|
* @description Allows you to use state to dynamically update your component
|
|
534
684
|
*/
|
|
535
685
|
export const useState = (initialState) => {
|
|
686
|
+
let key = ''
|
|
536
687
|
let value = initialState;
|
|
537
688
|
if (key && !states[key]) {
|
|
538
689
|
this.states[key] = initialState;
|
|
@@ -540,6 +691,16 @@ export const useState = (initialState) => {
|
|
|
540
691
|
return [value, (newValue) => {}];
|
|
541
692
|
};
|
|
542
693
|
|
|
694
|
+
/**
|
|
695
|
+
* @method useReducer
|
|
696
|
+
* @param {*} initialState
|
|
697
|
+
* @param {*} reducer
|
|
698
|
+
* @returns {Array} [value, set]
|
|
699
|
+
*/
|
|
700
|
+
export const useReducer = (/**@type {*}**/initialState, /**@type {function}**/reducer) => {
|
|
701
|
+
return [initialState, (newValue) => {}];
|
|
702
|
+
};
|
|
703
|
+
|
|
543
704
|
const constants = {};
|
|
544
705
|
let constantCounter = 0;
|
|
545
706
|
|
|
@@ -551,12 +712,20 @@ export const constant = (value) => {
|
|
|
551
712
|
return constants[key];
|
|
552
713
|
};
|
|
553
714
|
|
|
715
|
+
export const useRef = (initialState) => {
|
|
716
|
+
return {
|
|
717
|
+
current: initialState,
|
|
718
|
+
bind: '',
|
|
719
|
+
};
|
|
720
|
+
};
|
|
554
721
|
export default {
|
|
555
722
|
Component,
|
|
556
723
|
require,
|
|
557
724
|
invoke,
|
|
558
725
|
mem,
|
|
559
726
|
constant,
|
|
727
|
+
useRef,
|
|
728
|
+
useReducer,
|
|
560
729
|
useState,
|
|
561
730
|
strictMount,
|
|
562
731
|
stylis,
|
package/package.json
CHANGED
package/runtime/index.html
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>Vaderjs v1.3.3</title>
|
|
7
7
|
<meta name="description" content="Vader.js is a modern web framework for building web applications.">
|
|
8
|
-
<link rel="shortcut icon" href="https://raw.githubusercontent.com/Postr-Inc/Vader.js/
|
|
8
|
+
<link rel="shortcut icon" href="https://raw.githubusercontent.com/Postr-Inc/Vader.js/main/logo.png" type="image/x-icon">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
|
11
11
|
<div id="root"></div>
|
package/runtime/router.js
CHANGED
package/runtime/vader.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
window.params = {};
|
|
3
3
|
window.Vader = {
|
|
4
|
-
version: "1.3.
|
|
4
|
+
version: "1.3.3",
|
|
5
5
|
};
|
|
6
6
|
|
|
7
7
|
let errors = {
|
|
@@ -43,16 +43,13 @@ let hasran = [];
|
|
|
43
43
|
let states = {};
|
|
44
44
|
let mounts = [];
|
|
45
45
|
export const strictMount = (key, callback) => {
|
|
46
|
-
|
|
47
|
-
if(
|
|
48
|
-
|
|
49
|
-
|
|
46
|
+
let timer = setInterval(() => {
|
|
47
|
+
if (document.querySelector('[key="' + key + '"]')) {
|
|
48
|
+
console.log('mounted')
|
|
49
|
+
clearInterval(timer);
|
|
50
50
|
callback();
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
hasran.push(callback.toString());
|
|
54
|
-
}
|
|
55
|
-
},0);
|
|
51
|
+
}
|
|
52
|
+
}, 120);
|
|
56
53
|
};
|
|
57
54
|
|
|
58
55
|
window.delegate = (event) => {
|
|
@@ -167,9 +164,93 @@ export class Component {
|
|
|
167
164
|
window.listeners = [];
|
|
168
165
|
this.functionMap = new Map();
|
|
169
166
|
this.freeMemoryFromFunctions();
|
|
167
|
+
this.checkIFMounted();
|
|
170
168
|
this.memoizes = []
|
|
169
|
+
|
|
170
|
+
this.vdom = []
|
|
171
|
+
|
|
171
172
|
this.children = []
|
|
173
|
+
let dom = new DOMParser().parseFromString(this.render(), 'text/html').body.firstChild;
|
|
174
|
+
dom.querySelectorAll('*').forEach((el) => {
|
|
175
|
+
let obj = {
|
|
176
|
+
el: el,
|
|
177
|
+
tag: el.tagName,
|
|
178
|
+
html: el.innerHTML,
|
|
179
|
+
content: el.textContent,
|
|
180
|
+
value: el.value,
|
|
181
|
+
attributes: el.attributes,
|
|
182
|
+
children: el.childNodes,
|
|
183
|
+
}
|
|
184
|
+
this.vdom.push(obj)
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Parent of the current component.
|
|
189
|
+
* @type {Component}
|
|
190
|
+
*/
|
|
191
|
+
this.parentNode = {}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Request object.
|
|
195
|
+
*/
|
|
196
|
+
this.request = {
|
|
197
|
+
/**
|
|
198
|
+
* @type {string}
|
|
199
|
+
* @description The headers for the current route
|
|
200
|
+
*/
|
|
201
|
+
headers:{},
|
|
202
|
+
/**
|
|
203
|
+
* @type {string}
|
|
204
|
+
* @description The method for the current route
|
|
205
|
+
*/
|
|
206
|
+
method: "GET",
|
|
207
|
+
/**
|
|
208
|
+
* @type {string}
|
|
209
|
+
* @description params for the given route /:id/:name etc
|
|
210
|
+
*/
|
|
211
|
+
params: {},
|
|
212
|
+
/**
|
|
213
|
+
* @type {string}
|
|
214
|
+
* @description path: current route path
|
|
215
|
+
*/
|
|
216
|
+
path: "",
|
|
217
|
+
/**
|
|
218
|
+
* @type {string}
|
|
219
|
+
* @description query: query object for the current route ?name=hello -> {name: 'hello'}
|
|
220
|
+
*/
|
|
221
|
+
query: {},
|
|
222
|
+
},
|
|
223
|
+
/**
|
|
224
|
+
* @type {string}
|
|
225
|
+
* @description The response object for the current route
|
|
226
|
+
*/
|
|
227
|
+
this.response = {
|
|
228
|
+
/**
|
|
229
|
+
* @method json
|
|
230
|
+
* @description This method allows you to send json data to the client
|
|
231
|
+
* @param {*} data
|
|
232
|
+
*/
|
|
233
|
+
json: (data) => {},
|
|
234
|
+
/**
|
|
235
|
+
* @method send
|
|
236
|
+
* @description This method allows you to send text data to the client
|
|
237
|
+
* @param {*} data
|
|
238
|
+
*/
|
|
239
|
+
send: (data) => {},
|
|
240
|
+
/**
|
|
241
|
+
* @method redirect
|
|
242
|
+
* @description This method allows you to redirect the client to a new route
|
|
243
|
+
* @param {*} path
|
|
244
|
+
*/
|
|
245
|
+
redirect: (path) => {},
|
|
246
|
+
/**
|
|
247
|
+
* @method render
|
|
248
|
+
* @description render a new component to the client
|
|
249
|
+
* @param {*} Component
|
|
250
|
+
*/
|
|
251
|
+
render: async (Component) => {},
|
|
172
252
|
}
|
|
253
|
+
}
|
|
173
254
|
|
|
174
255
|
createComponent(/**@type {Component}**/component, props, children) {
|
|
175
256
|
|
|
@@ -179,26 +260,16 @@ export class Component {
|
|
|
179
260
|
if(!props.key){
|
|
180
261
|
throw new Error('new components must have a key')
|
|
181
262
|
}
|
|
182
|
-
let comp = new component();
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
if (newFunc) {
|
|
187
|
-
newFunc = newFunc[0].replace(/this.bind\((.*)\)/gs, "$1");
|
|
188
|
-
newFunc = newFunc.split('`')[1]
|
|
189
|
-
props[key] = this.bind(newFunc, { jsx: true, params: props[key], ref: props.ref });
|
|
190
|
-
return
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
props[key] = props[key].toString().replace('"', '').replace("'", '').replace('${', '').replace('}', '')
|
|
196
|
-
});
|
|
263
|
+
let comp = new component(props);
|
|
264
|
+
if(this.components[props.key]){
|
|
265
|
+
return this.components[props.key]
|
|
266
|
+
}
|
|
197
267
|
comp['props'] = props;
|
|
198
268
|
comp.children = children;
|
|
199
269
|
comp.props.children = children.join('')
|
|
200
270
|
comp.parentNode = this;
|
|
201
271
|
comp.key = props.key || null;
|
|
272
|
+
|
|
202
273
|
this.components[props.key] = comp
|
|
203
274
|
this.children.push(comp)
|
|
204
275
|
return this.components[props.key]
|
|
@@ -215,6 +286,7 @@ export class Component {
|
|
|
215
286
|
}
|
|
216
287
|
|
|
217
288
|
let comp = this.components[component.key];
|
|
289
|
+
|
|
218
290
|
let h = comp.render()
|
|
219
291
|
|
|
220
292
|
if(h && h.split('>,').length > 1){
|
|
@@ -240,28 +312,75 @@ export class Component {
|
|
|
240
312
|
* Hydrates the component by updating the HTML content if it has changed.
|
|
241
313
|
* @private
|
|
242
314
|
*/
|
|
243
|
-
hydrate() {
|
|
244
|
-
if (this.key) {
|
|
245
|
-
|
|
246
|
-
const el = document.querySelector(`[key="${this.key}"]`);
|
|
247
315
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
316
|
+
domDifference(oldDom, newDom) {
|
|
317
|
+
let diff = [];
|
|
318
|
+
|
|
319
|
+
for (let i = 0; i < oldDom.length; i++) {
|
|
320
|
+
let oldEl = oldDom[i];
|
|
321
|
+
let newEl = newDom[i];
|
|
322
|
+
|
|
323
|
+
if (oldEl && newEl && !oldEl.isEqualNode(newEl)) {
|
|
324
|
+
diff.push({
|
|
325
|
+
type: 'replace',
|
|
326
|
+
old: oldEl,
|
|
327
|
+
new: newEl.cloneNode(true),
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return diff;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
updateChangedElements(diff) {
|
|
336
|
+
diff.forEach((change) => {
|
|
337
|
+
switch (change.type) {
|
|
338
|
+
case 'replace':
|
|
339
|
+
change.old.parentNode.replaceChild(change.new, change.old);
|
|
340
|
+
break;
|
|
341
|
+
case 'remove':
|
|
342
|
+
change.old.remove();
|
|
343
|
+
break;
|
|
344
|
+
case 'add':
|
|
345
|
+
change.old.appendChild(change.new.cloneNode(true));
|
|
346
|
+
break;
|
|
347
|
+
default:
|
|
348
|
+
break;
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
hydrate(hook) {
|
|
354
|
+
if (this.key) {
|
|
355
|
+
if (hook) {
|
|
356
|
+
let newDom = new DOMParser().parseFromString(this.render(), 'text/html').body.firstChild;
|
|
357
|
+
|
|
358
|
+
let oldElements = document.body.querySelectorAll(`[ref="${hook}"]`);
|
|
359
|
+
let newElements = newDom.querySelectorAll(`[ref="${hook}"]`);
|
|
360
|
+
let diff = this.domDifference(oldElements, newElements);
|
|
361
|
+
this.updateChangedElements(diff);
|
|
362
|
+
} else {
|
|
363
|
+
const targetElement = document.querySelector(`[key="${this.key}"]`);
|
|
364
|
+
if (targetElement) {
|
|
365
|
+
targetElement.innerHTML = this.render();
|
|
366
|
+
} else {
|
|
367
|
+
console.error('Target element not found.');
|
|
261
368
|
}
|
|
262
369
|
}
|
|
263
370
|
}
|
|
264
371
|
}
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
patch(oldElements, newElements) {
|
|
376
|
+
const diff = this.domDifference(oldElements, newElements);
|
|
377
|
+
|
|
378
|
+
this.updateChangedElements(diff);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
|
|
265
384
|
|
|
266
385
|
/**
|
|
267
386
|
* Handles an object by parsing it as JSON and evaluating it.
|
|
@@ -279,7 +398,7 @@ export class Component {
|
|
|
279
398
|
}
|
|
280
399
|
|
|
281
400
|
/**
|
|
282
|
-
* Frees memory from functions that have not been used for a certain period of time.
|
|
401
|
+
* Frees memory from functions that have not been used for a certain period of time.c
|
|
283
402
|
* @private
|
|
284
403
|
*/
|
|
285
404
|
freeMemoryFromFunctions() {
|
|
@@ -299,23 +418,47 @@ export class Component {
|
|
|
299
418
|
* @param {string} ref - The reference.
|
|
300
419
|
* @returns {string} - A valid inline JS function call.
|
|
301
420
|
*/
|
|
302
|
-
bind(funcData,
|
|
421
|
+
bind(funcData,jsx,ref, paramNames, ...params) {
|
|
422
|
+
|
|
303
423
|
|
|
304
424
|
const name = `func_${crypto ? crypto.getRandomValues(new Uint32Array(1))[0] : Math.random()}`;
|
|
305
425
|
|
|
306
|
-
var dynamicFunction = (params) => {
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
426
|
+
var dynamicFunction = async (...params) => {
|
|
427
|
+
|
|
428
|
+
// Replace double commas with a single comma
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
// Turn params into a destructured object
|
|
432
|
+
let paramObject = {};
|
|
433
|
+
params = params[0]
|
|
434
|
+
|
|
435
|
+
paramNames = paramNames.replace(/,,/g, ',');
|
|
436
|
+
let newparamnames = paramNames.replaceAll(',,', ',')
|
|
437
|
+
// Remove trailing commas
|
|
438
|
+
paramNames.endsWith(',') ? paramNames = paramNames.slice(0, -1) : null;
|
|
439
|
+
console.log(paramNames)
|
|
440
|
+
|
|
441
|
+
params.forEach((param, index) => {
|
|
442
|
+
paramObject[newparamnames.split(',')[index]] = param;
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
paramNames = paramNames.replace(',,', ',');
|
|
446
|
+
let func = new Function(`${paramNames}`, `
|
|
447
|
+
return (async (${paramNames}) => {
|
|
448
|
+
${funcData}
|
|
449
|
+
})(${Object.keys(paramObject).join(',')})
|
|
450
|
+
`);
|
|
451
|
+
|
|
452
|
+
// Bind and execute the function with the provided parameters
|
|
453
|
+
await func.bind(this)(...Object.values(paramObject));
|
|
312
454
|
};
|
|
455
|
+
|
|
313
456
|
|
|
314
457
|
dynamicFunction = dynamicFunction.bind(this);
|
|
315
458
|
if (!this.functionMap.has(name)) {
|
|
316
459
|
document.addEventListener(`call_${name}`, (e) => {
|
|
317
460
|
|
|
318
|
-
dynamicFunction();
|
|
461
|
+
dynamicFunction(params);
|
|
319
462
|
this.functionMap.set(e.detail.name, {
|
|
320
463
|
lastUsed: Date.now(),
|
|
321
464
|
});
|
|
@@ -335,15 +478,17 @@ export class Component {
|
|
|
335
478
|
};
|
|
336
479
|
|
|
337
480
|
// Return a valid inline js function call
|
|
338
|
-
return
|
|
481
|
+
return jsx ? dynamicFunction : `
|
|
339
482
|
((event) => {
|
|
340
|
-
event.target.setAttribute('data-ref', '${
|
|
483
|
+
event.target.setAttribute('data-ref', '${ref}');
|
|
341
484
|
let reference = event.target.getAttribute('data-ref');
|
|
342
485
|
event.target.eventData = event;
|
|
486
|
+
|
|
343
487
|
let domquery = queryRef(reference);
|
|
344
|
-
|
|
345
|
-
domquery.eventData
|
|
346
|
-
|
|
488
|
+
|
|
489
|
+
domquery.eventData = event;
|
|
490
|
+
domquery.eventData.target = domquery;
|
|
491
|
+
call('${name}', {event:domquery.eventData}, '${paramNames}')
|
|
347
492
|
})(event)
|
|
348
493
|
`;
|
|
349
494
|
}
|
|
@@ -412,11 +557,14 @@ export class Component {
|
|
|
412
557
|
if (!this.state[key]) {
|
|
413
558
|
this.state[key] = initialState;
|
|
414
559
|
}
|
|
415
|
-
|
|
416
|
-
const
|
|
560
|
+
let updatedValue = () => this.state[key];
|
|
561
|
+
const getValue = updatedValue()
|
|
562
|
+
const set = (newValue, hook) => {
|
|
417
563
|
this.state[key] = newValue;
|
|
418
|
-
this.hydrate();
|
|
564
|
+
this.hydrate(hook);
|
|
419
565
|
};
|
|
566
|
+
this[`set${key}`] = set;
|
|
567
|
+
this[`get${key}`] = getValue;
|
|
420
568
|
return [getValue, set];
|
|
421
569
|
}
|
|
422
570
|
|
|
@@ -429,9 +577,11 @@ export class Component {
|
|
|
429
577
|
this.state[key] = newValue;
|
|
430
578
|
this.hydrate();
|
|
431
579
|
};
|
|
580
|
+
|
|
581
|
+
|
|
432
582
|
return {
|
|
433
|
-
bind: key,
|
|
434
|
-
current:
|
|
583
|
+
bind: key + this.key,
|
|
584
|
+
current: document.querySelector(`[ref="${key + this.key}"]`) || initialState
|
|
435
585
|
}
|
|
436
586
|
}
|
|
437
587
|
|
|
@@ -455,7 +605,7 @@ export class Component {
|
|
|
455
605
|
* @private
|
|
456
606
|
*/
|
|
457
607
|
checkIFMounted() {
|
|
458
|
-
if (this.mounted) return;
|
|
608
|
+
if (this.mounted || !this.key) return;
|
|
459
609
|
let timer = setInterval(() => {
|
|
460
610
|
if (document.querySelector('[key="' + this.key + '"]')) {
|
|
461
611
|
clearInterval(timer);
|
|
@@ -533,6 +683,7 @@ window.require = require;
|
|
|
533
683
|
* @description Allows you to use state to dynamically update your component
|
|
534
684
|
*/
|
|
535
685
|
export const useState = (initialState) => {
|
|
686
|
+
let key = ''
|
|
536
687
|
let value = initialState;
|
|
537
688
|
if (key && !states[key]) {
|
|
538
689
|
this.states[key] = initialState;
|
|
@@ -540,6 +691,16 @@ export const useState = (initialState) => {
|
|
|
540
691
|
return [value, (newValue) => {}];
|
|
541
692
|
};
|
|
542
693
|
|
|
694
|
+
/**
|
|
695
|
+
* @method useReducer
|
|
696
|
+
* @param {*} initialState
|
|
697
|
+
* @param {*} reducer
|
|
698
|
+
* @returns {Array} [value, set]
|
|
699
|
+
*/
|
|
700
|
+
export const useReducer = (/**@type {*}**/initialState, /**@type {function}**/reducer) => {
|
|
701
|
+
return [initialState, (newValue) => {}];
|
|
702
|
+
};
|
|
703
|
+
|
|
543
704
|
const constants = {};
|
|
544
705
|
let constantCounter = 0;
|
|
545
706
|
|
|
@@ -551,12 +712,20 @@ export const constant = (value) => {
|
|
|
551
712
|
return constants[key];
|
|
552
713
|
};
|
|
553
714
|
|
|
715
|
+
export const useRef = (initialState) => {
|
|
716
|
+
return {
|
|
717
|
+
current: initialState,
|
|
718
|
+
bind: '',
|
|
719
|
+
};
|
|
720
|
+
};
|
|
554
721
|
export default {
|
|
555
722
|
Component,
|
|
556
723
|
require,
|
|
557
724
|
invoke,
|
|
558
725
|
mem,
|
|
559
726
|
constant,
|
|
727
|
+
useRef,
|
|
728
|
+
useReducer,
|
|
560
729
|
useState,
|
|
561
730
|
strictMount,
|
|
562
731
|
stylis,
|
package/vader.js
CHANGED
|
@@ -25,23 +25,21 @@ function Compiler(func) {
|
|
|
25
25
|
let savedfuncnames = [];
|
|
26
26
|
let functions = string
|
|
27
27
|
.match(
|
|
28
|
-
/(?:const|let
|
|
28
|
+
/(?:const|let)\s*([a-zA-Z0-9_-]+)\s*=\s*function\s*\(([^)]*)\)|function\s*([a-zA-Z0-9_-]+)\s*\(([^)]*)\)/gs
|
|
29
29
|
)
|
|
30
30
|
?.map((match) => match.trim());
|
|
31
31
|
|
|
32
|
-
|
|
33
32
|
let functionNames = [];
|
|
34
33
|
|
|
35
34
|
if (functions) {
|
|
36
35
|
functions.forEach((func) => {
|
|
37
36
|
if (
|
|
38
37
|
!func.match(
|
|
39
|
-
/(?:const|let
|
|
38
|
+
/(?:const|let)\s*([a-zA-Z0-9_-]+)\s*=\s*function\s*\(([^)]*)\)|function\s*([a-zA-Z0-9_-]+)\s*\(([^)]*)\)/gs
|
|
40
39
|
)
|
|
41
40
|
) {
|
|
42
41
|
return;
|
|
43
|
-
}
|
|
44
|
-
let code = string;
|
|
42
|
+
}
|
|
45
43
|
|
|
46
44
|
let name = func.split(" ")[1].split("(")[0].trim();
|
|
47
45
|
|
|
@@ -73,6 +71,7 @@ function Compiler(func) {
|
|
|
73
71
|
let childs = [];
|
|
74
72
|
|
|
75
73
|
|
|
74
|
+
|
|
76
75
|
function extractAttributes(code) {
|
|
77
76
|
// Match elements with opening tags
|
|
78
77
|
const elementRegex = /<([a-zA-Z0-9_-]+)([^>]*)>/gs;
|
|
@@ -134,12 +133,11 @@ function Compiler(func) {
|
|
|
134
133
|
if (functionNames.length > 0) {
|
|
135
134
|
functionNames.forEach((name) => {
|
|
136
135
|
string.split("\n").forEach((line) => {
|
|
137
|
-
if (line.includes(name) && line.includes("function")
|
|
136
|
+
if (line.includes(name) && line.includes("function")) {
|
|
138
137
|
line = line.trim();
|
|
139
138
|
line = line.replace(/\s+/g, " ");
|
|
140
139
|
|
|
141
|
-
let ps =
|
|
142
|
-
|
|
140
|
+
let ps = line.split("(").slice(1).join("(").split(")")[0].trim();
|
|
143
141
|
|
|
144
142
|
// remove comments
|
|
145
143
|
ps = ps.match(/\/\*.*\*\//gs)
|
|
@@ -160,8 +158,7 @@ function Compiler(func) {
|
|
|
160
158
|
let params = hasParams
|
|
161
159
|
? line.split("(")[1].split(")")[0].trim()
|
|
162
160
|
: null;
|
|
163
|
-
|
|
164
|
-
|
|
161
|
+
|
|
165
162
|
if (
|
|
166
163
|
functionparams.length > 0 &&
|
|
167
164
|
functionparams.find((p) => p.name == name)
|
|
@@ -187,11 +184,10 @@ function Compiler(func) {
|
|
|
187
184
|
} ${params || null}${isJSXComponent ? "" : ","} true ${isJSXComponent ? "" : ","
|
|
188
185
|
} '${ref}')`;
|
|
189
186
|
|
|
190
|
-
console.log(replacement)
|
|
191
187
|
newvalue = newvalue.replace(
|
|
192
|
-
|
|
188
|
+
hasParams ? `${name}(${params})` : name,
|
|
193
189
|
`this.callFunction(\${${replacement}}, ${isJSXComponent ? true : false
|
|
194
|
-
}, event
|
|
190
|
+
}, event, \${JSON.stringify(${params || null})})`
|
|
195
191
|
);
|
|
196
192
|
}
|
|
197
193
|
}
|
|
@@ -218,14 +214,21 @@ function Compiler(func) {
|
|
|
218
214
|
|
|
219
215
|
|
|
220
216
|
let otherdata = {};
|
|
217
|
+
|
|
221
218
|
params ? (otherdata["params"] = params) : null;
|
|
222
219
|
otherdata["jsx"] = isJSXComponent;
|
|
223
220
|
otherdata["ref"] = ref;
|
|
224
221
|
|
|
225
222
|
newvalue = newvalue.split('\n').map(line => line.trim() ? line.trim() + ';' : line).join('\n');
|
|
226
223
|
|
|
227
|
-
|
|
224
|
+
// turn params into param1, param2, param3
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
let paramString = params ? params.split(' ').map(param => param + ',').join('') : "";
|
|
228
228
|
|
|
229
|
+
let newatribute = `${attributeName}="\${this.bind(\`${newvalue}\`, ${isJSXComponent ? true : false}, '${ref}', "${paramString}", ${params || null})}"`
|
|
230
|
+
|
|
231
|
+
|
|
229
232
|
attribute[attributeName] = {
|
|
230
233
|
old: old,
|
|
231
234
|
new: newatribute,
|
|
@@ -259,9 +262,10 @@ function Compiler(func) {
|
|
|
259
262
|
otherdata["ref"] = ref;
|
|
260
263
|
// since js is all in one line split it
|
|
261
264
|
newvalue = newvalue.split('\n').map(line => line.trim() ? line.trim() + ';' : line).join('\n');
|
|
262
|
-
|
|
263
|
-
let
|
|
264
|
-
|
|
265
|
+
// param1, param2, param3
|
|
266
|
+
let length = params ? params.split(' ').length : 0;
|
|
267
|
+
let paramString = params ? params.split(' ').map(param => param + ',').join('') : "";
|
|
268
|
+
let newattribute = `${attributeName}="\${this.bind(\`${newvalue}\`, ${isJSXComponent ? true : false}, '${ref}', "${paramString}", ${params || null})}"`
|
|
265
269
|
newattribute = newattribute.replace(/\s+/g, " ")
|
|
266
270
|
string = string.replace(old, newattribute);
|
|
267
271
|
}
|
|
@@ -298,6 +302,7 @@ function Compiler(func) {
|
|
|
298
302
|
let contents = "";
|
|
299
303
|
let updatedContents = "";
|
|
300
304
|
outerReturn.forEach((returnStatement) => {
|
|
305
|
+
|
|
301
306
|
let lines = returnStatement.split("\n");
|
|
302
307
|
|
|
303
308
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -306,7 +311,7 @@ function Compiler(func) {
|
|
|
306
311
|
continue;
|
|
307
312
|
}
|
|
308
313
|
contents += line + "\n";
|
|
309
|
-
}
|
|
314
|
+
}
|
|
310
315
|
|
|
311
316
|
// Remove trailing ']'
|
|
312
317
|
contents = contents.trim().replace(/\]$/, "");
|
|
@@ -324,24 +329,25 @@ function Compiler(func) {
|
|
|
324
329
|
|
|
325
330
|
let value = attributes[key];
|
|
326
331
|
let oldvalue = value;
|
|
327
|
-
if (value && !value.new) {
|
|
328
|
-
if (value && value.includes("{")) {
|
|
332
|
+
if (value && !value.new) {
|
|
333
|
+
if (value && value.includes("={")) {
|
|
329
334
|
value = value.replace("=", "");
|
|
330
335
|
value == "undefined" ? (value = '"') : (value = value);
|
|
331
336
|
key == 'style' ? value = `{this.parseStyle({${value.split('{{')[1].split('}}')[0]}})}` : null
|
|
332
337
|
|
|
333
|
-
|
|
338
|
+
|
|
339
|
+
value = `="\$${value}",`;
|
|
334
340
|
string = string.replace(oldvalue, value);
|
|
335
341
|
|
|
336
|
-
} else if (value && value.includes("`")) {
|
|
342
|
+
} else if (value && value.includes("={`")) {
|
|
337
343
|
value = value.replace("=", "");
|
|
338
|
-
|
|
339
|
-
value = `"\$${value}"
|
|
344
|
+
|
|
345
|
+
value = `"\$${value}",`;
|
|
340
346
|
string = string.replace(oldvalue, value);
|
|
341
347
|
|
|
342
348
|
}
|
|
343
349
|
} else if (value && value.new) {
|
|
344
|
-
let newvalue = value.new;
|
|
350
|
+
let newvalue = value.new + ",";
|
|
345
351
|
let old = value.old;
|
|
346
352
|
string = string.replace(old, newvalue);
|
|
347
353
|
}
|
|
@@ -407,16 +413,15 @@ function Compiler(func) {
|
|
|
407
413
|
let setKey = line.split("=")[0].split(",")[1].trim().replace("]", "");
|
|
408
414
|
|
|
409
415
|
key = key.replace("[", "").replace(",", "");
|
|
410
|
-
let value = line
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
this.${key} = ${key}
|
|
418
|
-
this.${setKey} = ${setKey}
|
|
416
|
+
let value = line.split("=")[1].split("useState(")[1]
|
|
417
|
+
let regex = /useState\((.*)\)/gs
|
|
418
|
+
value = value.match(regex) ? value.match(regex)[0].split("useState(")[1].split(")")[0].trim() : value
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
let newState = `${type} [${key}, ${setKey}] = this.useState('${key}', ${value}
|
|
422
|
+
|
|
419
423
|
`;
|
|
424
|
+
|
|
420
425
|
|
|
421
426
|
// get setkey calls and replace with this.setKey
|
|
422
427
|
string.split("\n").forEach((line) => {
|
|
@@ -431,18 +436,28 @@ function Compiler(func) {
|
|
|
431
436
|
}
|
|
432
437
|
});
|
|
433
438
|
string = string.replace(line, newState);
|
|
434
|
-
} else if (line.includes("useRef")) {
|
|
439
|
+
} else if (line.includes("useRef") && !line.includes("import")) {
|
|
435
440
|
line = line.trim();
|
|
436
441
|
// let ref = useRef(null)
|
|
437
442
|
let type = line.split(" ")[0];
|
|
438
443
|
let key = line.split("=")[0].split(" ")[1].trim();
|
|
439
|
-
let value = line
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
444
|
+
let value = line.split("=")[1].split("useRef(")[1]
|
|
445
|
+
|
|
446
|
+
let regex = /useState\((.*)\)/gs
|
|
447
|
+
value = value.match(regex) ? value.match(regex)[0].split("useRef(")[1].split(")")[0].trim() : value
|
|
448
|
+
let newState = `${type} ${key} = this.useRef('${key}', ${value}`;
|
|
449
|
+
|
|
450
|
+
string = string.replace(line, newState);
|
|
451
|
+
}else if (line.includes("useReducer") && !line.includes("import")){
|
|
452
|
+
line = line.trim();
|
|
453
|
+
// let ref = useRef(null)
|
|
454
|
+
let type = line.split(" ")[0];
|
|
455
|
+
let key = line.split("=")[0].split(" ")[1].trim();
|
|
456
|
+
let value = line.split("=")[1].split("useReducer(")[1]
|
|
457
|
+
|
|
458
|
+
let regex = /useReducer\((.*)\)/gs
|
|
459
|
+
value = value.match(regex) ? value.match(regex)[0].split("useReducer(")[1].split(")")[0].trim() : value
|
|
460
|
+
let newState = `${type} ${key} = this.useReducer('${key}', ${value}`;
|
|
446
461
|
|
|
447
462
|
string = string.replace(line, newState);
|
|
448
463
|
}
|
|
@@ -477,7 +492,11 @@ function Compiler(func) {
|
|
|
477
492
|
return array;
|
|
478
493
|
};
|
|
479
494
|
|
|
480
|
-
|
|
495
|
+
// replaceall comments ${/* */} with ''
|
|
496
|
+
string = string.replaceAll(/\$\{\/\*.*\*\/\}/gs, "");
|
|
497
|
+
|
|
498
|
+
string = string.replaceAll('../src', './src')
|
|
499
|
+
|
|
481
500
|
|
|
482
501
|
// capture <Component />, <Component></Component>, and <Component>content</Component>
|
|
483
502
|
|
|
@@ -498,29 +517,9 @@ function Compiler(func) {
|
|
|
498
517
|
let myChildrens = [];
|
|
499
518
|
|
|
500
519
|
let name = component.split("<")[1].split(">")[0].split(" ")[0].replace("/", "");
|
|
501
|
-
let props = component.split(`<${name}`)[1].split(">")[0].trim()
|
|
502
|
-
function parseProps(attributes) {
|
|
503
|
-
console.log(attributes)
|
|
504
|
-
let props = {};
|
|
505
|
-
if (attributes) {
|
|
506
|
-
let propsMatch;
|
|
507
|
-
let regex = /([a-zA-Z0-9_-]+)(\s*=\s*("([^"\\]*(\\.[^"\\]*)*)"|'([^'\\]*(\\.[^'\\]*)*)'|\{(?:[^{}]|(?:\{(?:[^{}]|(?:\{[^{}]*\})*)*\})*)*\}|(?:\([^)]*\)|\{[^}]*\}|()=>\s*(?:\{[^}]*\})?)|\[[^\]]*\]))?/gs
|
|
508
|
-
while ((propsMatch = regex.exec(attributes)) !== null) {
|
|
509
|
-
let [, attributeName, attributeValue] = propsMatch;
|
|
510
|
-
console.log(attributeName, attributeValue)
|
|
511
|
-
attributeValue = attributeValue.replaceAll('="', '').replace("='", '')
|
|
512
|
-
if(attributeValue.startsWith("'")){
|
|
513
|
-
attributeValue = attributeValue.replace("'", '')
|
|
514
|
-
}
|
|
515
|
-
props[attributeName] = attributeValue || null;
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
return props;
|
|
521
|
-
}
|
|
522
|
-
|
|
520
|
+
let props = component.split(`<${name}`)[1].split(">")[0].trim()
|
|
523
521
|
|
|
522
|
+
|
|
524
523
|
let savedname = name;
|
|
525
524
|
let children = props
|
|
526
525
|
? component
|
|
@@ -555,14 +554,36 @@ function Compiler(func) {
|
|
|
555
554
|
myChildrens.push(child.children);
|
|
556
555
|
}
|
|
557
556
|
});
|
|
557
|
+
|
|
558
|
+
|
|
559
|
+
|
|
560
|
+
|
|
561
|
+
props = props
|
|
562
|
+
.replaceAll("=", ":")
|
|
563
|
+
.replaceAll('"', "'")
|
|
564
|
+
//fix key:'value' to key:'value', only if value ="value"
|
|
565
|
+
|
|
566
|
+
.replaceAll(",,", ',')
|
|
567
|
+
.replaceAll("className", "class")
|
|
568
|
+
.replaceAll("classname", "class")
|
|
569
|
+
.replaceAll("'${", "")
|
|
570
|
+
.replaceAll("}'", "")
|
|
571
|
+
.split("$:")
|
|
572
|
+
.join("")
|
|
573
|
+
.replace('/', '')
|
|
574
|
+
// remove trailing ,
|
|
575
|
+
.replace(/,\s*$/, "")
|
|
576
|
+
props = props.replace(/:('[^']*'|"[^"]*")/g, ':$1,');
|
|
577
|
+
props = props.replaceAll(',,', ',')
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
|
|
558
581
|
|
|
559
|
-
props = JSON.stringify(parseProps(props));
|
|
560
|
-
|
|
561
582
|
let replace = "";
|
|
562
583
|
replace = isChild
|
|
563
|
-
? `this.memoize(this.createComponent(${savedname.replaceAll('/', '')}, ${props}
|
|
584
|
+
? `this.memoize(this.createComponent(${savedname.replaceAll('/', '')}, ${props}, [${myChildrens.length > 0 ? myChildrens.join(",") : ""
|
|
564
585
|
}])),`
|
|
565
|
-
: `\${this.memoize(this.createComponent(${savedname.replaceAll('/', '')}, ${props}
|
|
586
|
+
: `\${this.memoize(this.createComponent(${savedname.replaceAll('/', '')}, {${props}}, [${myChildrens.length > 0 ? myChildrens.join(",") : ""
|
|
566
587
|
}]))}`;
|
|
567
588
|
|
|
568
589
|
body = body.replace(before, replace);
|
|
@@ -573,6 +594,7 @@ function Compiler(func) {
|
|
|
573
594
|
|
|
574
595
|
|
|
575
596
|
|
|
597
|
+
string = string.replaceAll('vaderjs/client', './vader.js')
|
|
576
598
|
string = string.replaceAll("<>", "`").replaceAll("</>", "`");
|
|
577
599
|
string = parseComponents(string);
|
|
578
600
|
|
|
@@ -633,10 +655,8 @@ async function Build() {
|
|
|
633
655
|
|
|
634
656
|
// Read and compile file content
|
|
635
657
|
let data = await fs.readFileSync(origin, "utf8");
|
|
636
|
-
data = Compiler(data);
|
|
637
|
-
|
|
638
|
-
// Write compiled content to the 'dist/pages' directory
|
|
639
|
-
console.log(`Compiling ${fileName} to ${obj.url}`)
|
|
658
|
+
data = Compiler(data);
|
|
659
|
+
|
|
640
660
|
await writer(process.cwd() + "/dist/pages/" + fileName, data);
|
|
641
661
|
|
|
642
662
|
// Generate routing logic
|
|
@@ -666,12 +686,13 @@ async function Build() {
|
|
|
666
686
|
absolute: true,
|
|
667
687
|
});
|
|
668
688
|
scannedVaderFiles.forEach(async (file) => {
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
}
|
|
673
|
-
|
|
689
|
+
file = file.replace(/\\/g, '/');
|
|
690
|
+
|
|
691
|
+
|
|
674
692
|
let name = file.split( '/node_modules/vaderjs/runtime/')[1]
|
|
693
|
+
if(file.includes('index.html') && fs.existsSync(process.cwd() + "/dist/" + name)){
|
|
694
|
+
return
|
|
695
|
+
}
|
|
675
696
|
let data = await reader(file)
|
|
676
697
|
bundleSize += fs.statSync(file).size;
|
|
677
698
|
await writer(process.cwd() + "/dist/" + name, data);
|
|
@@ -709,21 +730,25 @@ async function Build() {
|
|
|
709
730
|
})
|
|
710
731
|
|
|
711
732
|
if (!fs.existsSync(process.cwd() + "/dist/index.html")) {
|
|
733
|
+
|
|
712
734
|
scannedFiles.forEach(async (file) => {
|
|
713
735
|
file = file.split(process.cwd() + '/runtime/')[1]
|
|
714
736
|
|
|
715
737
|
if (file === "app.js") {
|
|
716
738
|
return
|
|
717
739
|
}
|
|
740
|
+
console.log(`Compiling ${file} to /dist/${file}`)
|
|
741
|
+
if(file.includes('index.html') && fs.existsSync(process.cwd() + "/runtime/" + file)){
|
|
742
|
+
console.log(`Compiling ${file} to /dist/${file}`)
|
|
743
|
+
return
|
|
744
|
+
}
|
|
718
745
|
bundleSize += fs.statSync(process.cwd() + "/runtime/" + file).size;
|
|
719
746
|
let data = await reader(process.cwd() + "/runtime/" + file)
|
|
720
747
|
await writer(process.cwd() + "/dist/" + file, data);
|
|
721
748
|
});
|
|
722
749
|
|
|
723
750
|
}
|
|
724
|
-
|
|
725
751
|
|
|
726
|
-
console.log(`Compilation completed`)
|
|
727
752
|
globalThis.isBuilding = false
|
|
728
753
|
}
|
|
729
754
|
import { watch } from "fs";
|
|
@@ -747,13 +772,17 @@ Vader.js v1.3.3
|
|
|
747
772
|
}
|
|
748
773
|
},
|
|
749
774
|
);
|
|
750
|
-
const watcher2 = watch(
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
) {
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
775
|
+
const watcher2 = watch(
|
|
776
|
+
process.cwd() + '/src',
|
|
777
|
+
{ recursive: true },
|
|
778
|
+
(event, filename) => {
|
|
779
|
+
if (event == 'change'
|
|
780
|
+
&& !globalThis.isBuilding
|
|
781
|
+
) {
|
|
782
|
+
Build()
|
|
783
|
+
}
|
|
784
|
+
},
|
|
785
|
+
);
|
|
757
786
|
watcher2.on('error', (err) => console.log(err))
|
|
758
787
|
watcher.on('error', (err) => console.log(err))
|
|
759
788
|
|