vaderjs 1.3.3-alpha-4 → 1.3.3-alpha-6

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 CHANGED
@@ -14,37 +14,24 @@
14
14
 
15
15
 
16
16
 
17
- ## Get Started
17
+ ## Get Started
18
18
 
19
- 1. Install Bun.js
19
+ 2. Install vaderjs
20
20
 
21
- Need more help? [Bun guide](https://bun.sh/docs/installation)
22
-
23
- ```sh
24
- curl -fsSL https://bun.sh/install | bash # for macOS, Linux, and WSL
21
+ ```bash
22
+ npm i vaderjs@latest
25
23
  ```
26
24
 
27
- > Skip if you are not using windows!
28
- [How to open folder in wsl](https://code.visualstudio.com/docs/remote/wsl)
29
-
30
- 1. Open a WSL terminal window (using the start menu item or by typing wsl from a command prompt / PowerShell).
31
-
32
- 2. Navigate to a folder you'd like to open in VS Code (including, but not limited to, Windows filesystem mounts like /mnt/c)
25
+ 3. Install five server - recommended to watch the index.html file as you edit your code
33
26
 
34
- 3. Type code . in the terminal. When doing this for the first time, you should see VS Code fetching components needed to run in WSL. This should only take a short while, and is only needed once.
27
+ [Vscode 5 Server](https://marketplace.visualstudio.com/items?itemName=yandeu.five-server)
35
28
 
36
- Open folder in vsc - open terminal and type wsl then type `code .` and continue to the next step
37
-
38
- 2. Installing vaderjs
39
-
40
- ```bash
41
- npm i vaderjs
42
- ```
43
-
44
- 3. Create Proper Folders
29
+ 4. Create Proper Folders
45
30
 
46
31
  Create a pages folder - which allows you to have nextjs page like routing via buns file based router
47
32
 
33
+ Tip: Each folder can be deep nested up to 4 levels!
34
+
48
35
  ```bash
49
36
  /pages/index.jsx = /
50
37
  /pages/home/[page].jsx = /home/:page
@@ -62,14 +49,13 @@ public - used for anything
62
49
 
63
50
 
64
51
 
65
- 4. And your done - Run `bun run vader --build` and the compiled output is visible inside of the `/dist/` folder!
52
+ 5. And your done - Run `npx vaderjs` and the compiled output is visible inside of the `/dist/` folder!
66
53
 
67
54
 
68
55
  ## Key Features & Examples
69
56
 
70
57
  ### File based routing
71
- vader's compiler automatically handles routing so you wont need to!
72
- below is valid paths for parsing per [Buns fileSystem Routing Api](https://bun.sh/docs/api/file-system-router)
58
+ vader's compiler automatically handles routing so you wont need to! - it uses a similar page routing to nextjs
73
59
 
74
60
  ```bash
75
61
  /pages/index.jsx = /
@@ -78,7 +64,7 @@ below is valid paths for parsing per [Buns fileSystem Routing Api](https://bun.s
78
64
 
79
65
 
80
66
  ```
81
- You can grab the request object from `this.request` and response object from `this.response!`
67
+ For pages that have [params] you can derive it using this.request
82
68
 
83
69
 
84
70
  ### Simplified Component Creation
@@ -145,6 +131,10 @@ Vaderjs allows you to bind functions directly to html elements just like react
145
131
  function click(event, otherparams){
146
132
  console.log(event.target, otherparams)
147
133
  }
134
+
135
+ const hello = function(event, otherparams){
136
+
137
+ }
148
138
 
149
139
  return <>
150
140
  <button onclick={()=>click()}>Click Me</button>
package/package.json CHANGED
@@ -2,12 +2,12 @@
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-4",
5
+ "version": "1.3.3-alpha-6",
6
6
  "bin": {
7
7
  "vader": "./vader.js"
8
8
  },
9
9
  "type": "module",
10
- "devDependencies": {
11
- "glob": "^10.3.10"
10
+ "dependencies": {
11
+ "glob": "latest"
12
12
  }
13
13
  }
@@ -0,0 +1,552 @@
1
+
2
+ window.params = {};
3
+ window.Vader = {
4
+ version: "1.3.2",
5
+ };
6
+
7
+ let errors = {
8
+ "SyntaxError: Unexpected token '<'": "You forgot to enclose tags in a fragment <></>",
9
+ }
10
+
11
+ const path = {
12
+ basename: (path) => {
13
+ return path.split("/").pop();
14
+ },
15
+
16
+ }
17
+
18
+ window.queryRef = (ref) => {
19
+ return document.querySelector(`[data-ref="${ref}"]`)
20
+ }
21
+ window.reinvoke = (eventtype, element) => {
22
+ const eventListener = (e) => {
23
+ return e
24
+ };
25
+
26
+ // Check if the event listener has not been added before adding it
27
+ if (!element._eventListenerAdded) {
28
+ element.addEventListener(eventtype, eventListener);
29
+
30
+ // Set the flag to indicate that the event listener has been added
31
+ element._eventListenerAdded = true;
32
+
33
+ // Trigger the event without overwriting existing data or listeners
34
+ element.dispatchEvent(new Event(eventtype));
35
+ }
36
+ };
37
+
38
+
39
+
40
+
41
+ let invokes = []
42
+ let hasran = [];
43
+ let states = {};
44
+ let mounts = [];
45
+ export const strictMount = (key, callback) => {
46
+ let interval = setInterval(() => {
47
+ if(mounts.find(mount => mount.key === key)
48
+ && !hasran.includes(callback.toString())
49
+ ){
50
+ callback();
51
+ clearInterval(interval)
52
+
53
+ hasran.push(callback.toString());
54
+ }
55
+ },0);
56
+ };
57
+
58
+ window.delegate = (event) => {
59
+ return event.detail.target
60
+ }
61
+
62
+ let components = {};
63
+
64
+ let style = document.createElement("style");
65
+ document.head.appendChild(style);
66
+
67
+ const parseStyles = async (styles, className = '') => {
68
+ let css = await fetch(styles).then((res) => res.text());
69
+ let classes = css.split("}");
70
+ let parsedClasses = {};
71
+ classes.forEach((cls) => {
72
+
73
+ let name = cls.split(".")[1];
74
+ let value = cls.split("{")[1]
75
+ let keys = value.split(";");
76
+ let newKeys = [];
77
+ keys.forEach((key) => {
78
+ if (key.includes(":")) {
79
+ let newKey = key.split(":")[0].trim();
80
+ let newValue = key.split(":")[1].trim();
81
+ newKeys.push(`${newKey}: "${newValue}"`);
82
+ }
83
+ });
84
+ value = `{${newKeys.join(",")}}`;
85
+
86
+
87
+ parsedClasses[name] = JSON.stringify(value);
88
+ });
89
+ return parsedClasses;
90
+ };
91
+
92
+
93
+ export const stylis = {
94
+ /**
95
+ * @method create
96
+ * @param {*} styles
97
+ * @returns {Object} classes
98
+ * @description This method allows you to create css classes from an object
99
+ */
100
+ create: async (/**@type {string} */ styles) => {
101
+
102
+ return await parseStyles(styles);
103
+ },
104
+ };
105
+
106
+ /**
107
+ * @method mem
108
+ * @param {Component} component
109
+ * @returns {Component} Stateless Component
110
+ * @description This method allows you to memoize a component - this means it will be intialized only once and can be reused multiple times baased on a static key
111
+ */
112
+ export const mem = (/**@type {Component}**/ component) => {
113
+ // ensure component is instance of Component
114
+ switch (true) {
115
+ case !(component instanceof Component):
116
+ throw new Error("component must be an instance of Component");
117
+ case !component.key:
118
+ throw new Error("component must have a static key");
119
+ // check if key was randomly generated
120
+ }
121
+ let key = component.key;
122
+ if (!components[key]) {
123
+ components[key] = component;
124
+ }
125
+
126
+ return components[key];
127
+ };
128
+
129
+ /**
130
+ * @method invoke
131
+ * @description This method allows you to invoke a function from its id
132
+ * @param {*} name
133
+ * @param {*} params
134
+ * @returns
135
+ * @example
136
+ * invoke(this.functions['test'], 'hello') // this will invoke the function test with the params hello
137
+ */
138
+
139
+ let functions = {};
140
+
141
+ export const invoke = (func, params) => {
142
+ let name = func.name;
143
+
144
+ window[name] = function (params) {
145
+ return func(params);
146
+ }
147
+ window[name] = window[name].bind(this);
148
+
149
+
150
+ return `${name}(${params})`;
151
+
152
+ };
153
+
154
+ /**
155
+ * Represents a component in the Vader framework.
156
+ */
157
+ export class Component {
158
+ /**
159
+ * Creates an instance of Component.
160
+ */
161
+ constructor() {
162
+ this.state = {};
163
+ this.key = null;
164
+ this.components = {};
165
+ this.mounted = false;
166
+ this.checkIFMounted();
167
+ this.currenthtml = null;
168
+ window.listeners = [];
169
+ this.functionMap = new Map();
170
+ this.freeMemoryFromFunctions();
171
+ this.memoizes = []
172
+ this.children = []
173
+ }
174
+
175
+ createComponent(/**@type {Component}**/component, props, children) {
176
+
177
+ if (!component) {
178
+ throw new Error("Component must be defined");
179
+ }
180
+ if(!props.key){
181
+ throw new Error('new components must have a key')
182
+ }
183
+ let comp = new component();
184
+
185
+ comp['props'] = props;
186
+ comp.children = children;
187
+ comp.props.children = children.join('')
188
+ comp.parentNode = this;
189
+ comp.key = props.key || null;
190
+ this.components[props.key] = comp
191
+ this.children.push(comp)
192
+ return this.components[props.key]
193
+ }
194
+ memoize(/**@type {Component}**/component){
195
+ if(!component.key){
196
+ throw new Error('Component must have a static key')
197
+ }
198
+ switch(true){
199
+ case !this.memoizes.includes(component.key):
200
+ this.memoizes.push(component.key)
201
+ this.components[component.key] = component;
202
+ break;
203
+ }
204
+
205
+ let comp = this.components[component.key];
206
+ let h = comp.render()
207
+
208
+ if(h && h.split('>,').length > 1){
209
+ h = h.replaceAll('>,', '>')
210
+ }
211
+
212
+ return `<div key="${component.key}">${h}</div>`
213
+ }
214
+ parseStyle(styles){
215
+ let css = ''
216
+ Object.keys(styles).forEach((key) => {
217
+ let value = styles[key]
218
+ key = key.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, '$1-$2').toLowerCase()
219
+ css += `${key}:${value};`
220
+ })
221
+ return css
222
+ }
223
+ bindMount(){
224
+ mounts.push(this)
225
+ }
226
+
227
+ /**
228
+ * Hydrates the component by updating the HTML content if it has changed.
229
+ * @private
230
+ */
231
+ hydrate() {
232
+ if (this.key) {
233
+
234
+ const el = document.querySelector(`[key="${this.key}"]`);
235
+
236
+
237
+ if (el) {
238
+
239
+
240
+ // Render the new HTML content
241
+ const newHtml = this.render();
242
+ // Compare the new HTML with the cached content
243
+ if (newHtml !== this.currentHtml) {
244
+ // Update the HTML only if it has changed
245
+ el.innerHTML = newHtml;
246
+
247
+ // Update the cached HTML content
248
+ this.currentHtml = newHtml;
249
+ }
250
+ }
251
+ }
252
+ }
253
+
254
+ /**
255
+ * Handles an object by parsing it as JSON and evaluating it.
256
+ * @param {string} obj - The object to handle.
257
+ * @returns {*} - The evaluated object.
258
+ * @prvate
259
+ */
260
+ handleObject(obj) {
261
+ try {
262
+ obj = JSON.parse(obj);
263
+ } catch (error) {
264
+ // Handle JSON parsing error if needed
265
+ }
266
+ return eval(obj);
267
+ }
268
+
269
+ /**
270
+ * Frees memory from functions that have not been used for a certain period of time.
271
+ * @private
272
+ */
273
+ freeMemoryFromFunctions() {
274
+ setInterval(() => {
275
+ for (var [key, value] in this.functionMap) {
276
+ if (Date.now() - value.lastUsed > 1000) {
277
+ this.functionMap.delete(key);
278
+ }
279
+ }
280
+ }, 1000);
281
+ }
282
+
283
+ /**
284
+ * Binds a function to the component.
285
+ * @param {string} funcData - The function data.
286
+ * @param {string} p - The parameter.
287
+ * @param {string} ref - The reference.
288
+ * @returns {string} - A valid inline JS function call.
289
+ */
290
+ bind(funcData, d) {
291
+
292
+ const name = `func_${crypto ? crypto.getRandomValues(new Uint32Array(1))[0] : Math.random()}`;
293
+
294
+ var dynamicFunction = (params) => {
295
+ let func = new Function(`return (async (${params}) => {
296
+ console.log('called')
297
+ ${funcData}
298
+ })()`);
299
+ func = func.bind(this);
300
+ func(params);
301
+ };
302
+
303
+ dynamicFunction = dynamicFunction.bind(this);
304
+ if (!this.functionMap.has(name)) {
305
+ document.addEventListener(`call_${name}`, (e) => {
306
+
307
+ dynamicFunction();
308
+ this.functionMap.set(e.detail.name, {
309
+ lastUsed: Date.now(),
310
+ });
311
+ });
312
+ }
313
+
314
+ this.functionMap.set(name, {
315
+ lastUsed: Date.now(),
316
+ });
317
+
318
+ window.call = (name, eventdata, params) => {
319
+ document.dispatchEvent(
320
+ new CustomEvent(`call_${name}`, {
321
+ detail: { name: `call_${name}`, target: eventdata },
322
+ })
323
+ );
324
+ };
325
+
326
+ // Return a valid inline js function call
327
+ return d.jsx ? dynamicFunction : `
328
+ ((event) => {
329
+ event.target.setAttribute('data-ref', '${d.ref}');
330
+ let reference = event.target.getAttribute('data-ref');
331
+ event.target.eventData = event;
332
+ let domquery = queryRef(reference);
333
+ domquery.eventData = event;
334
+ domquery.eventData.detail.target = domquery;
335
+ call('${name}', {event:domquery.eventData}, '${d.params}')
336
+ })(event)
337
+ `;
338
+ }
339
+
340
+ /**
341
+ * Calls a function with the specified parameters. and dispatches an event.
342
+ * @param {string} func - The function name.
343
+ * @param {...*} params - The function parameters.
344
+ */
345
+ callFunction(func, isInlineJsx, ...params) {
346
+ if(!isInlineJsx && params[0] && params[0].detail){
347
+ let el = params[0].detail.target.event.target
348
+ params[0].data = el.value;
349
+ params[0] = params[0].detail.target.event
350
+ }
351
+ func = func.replace(/'/g, '');
352
+ document.dispatchEvent(new CustomEvent(func, { detail: { name: func, params: params } }));
353
+ }
354
+
355
+ /**
356
+ * Uses a function with the specified parameters.
357
+ * @param {Function} func - The function to use.
358
+ * @param {string} params - The function parameters.
359
+ * @param {boolean} [isInlineJsx=false] - Indicates if the function is an inline JSX.
360
+ * @returns {string} - The function call.
361
+ */
362
+ useFunction(func, params, isInlineJsx = false) {
363
+ const sanitizedFuncName = func.name.trim().replace(/\s+/g, '_');
364
+
365
+ if (!invokes.includes(`'${sanitizedFuncName}'${this.key}`)) {
366
+ invokes.push(`'${sanitizedFuncName}'${this.key}`);
367
+ document.addEventListener(`call_${sanitizedFuncName}_${this.key}`, (e) => {
368
+ let { name, params } = e.detail;
369
+ if (name === `call_${sanitizedFuncName}_${this.key}`) {
370
+ let isarray = Array.isArray(params);
371
+
372
+ func(...(isarray ? params : [params]));
373
+ }
374
+ });
375
+
376
+ func = func.bind(this);
377
+ }
378
+
379
+ try {
380
+ params = JSON.parse(params);
381
+ } catch (error) {
382
+ // Handle JSON parsing error if needed
383
+ }
384
+
385
+ const returnString = isInlineJsx
386
+ ? `'call_${sanitizedFuncName}_${this.key}'`
387
+ : `document.dispatchEvent(new CustomEvent('call_${sanitizedFuncName}_${this.key}', { detail: { name: 'call_${sanitizedFuncName}_${this.key}', params: ${JSON.stringify(params)} } }))`;
388
+
389
+ return returnString;
390
+ }
391
+
392
+ /**
393
+ * Uses state to dynamically update the component.
394
+ * @method useState
395
+ * @param {string} [key=null] - The auto-generated key.
396
+ * @param {*} initialState - The initial state.
397
+ * @param {Component.render} [func=null] - The render function.
398
+ * @returns {Array} - An array containing the state value and the setter function.
399
+ */
400
+ useState(key = null, initialState) {
401
+ if (!this.state[key]) {
402
+ this.state[key] = initialState;
403
+ }
404
+ const getValue = () => this.state[key];
405
+ const set = (newValue) => {
406
+ this.state[key] = newValue;
407
+ this.hydrate();
408
+ };
409
+ return [getValue, set];
410
+ }
411
+
412
+ useRef(key = null, initialState) {
413
+ if (!this.state[key]) {
414
+ this.state[key] = initialState;
415
+ }
416
+ const getValue = () => this.state[key];
417
+ const set = (newValue) => {
418
+ this.state[key] = newValue;
419
+ this.hydrate();
420
+ };
421
+ return {
422
+ bind: key,
423
+ current: () => document.querySelector(`[ref="${key}"]`) || this.state[key],
424
+ }
425
+ }
426
+
427
+ useReducer(key = null, initialState, func = null) {
428
+ const getValue = () => this.state[key];
429
+ const set = (newValue) => {
430
+
431
+ this.hydrate();
432
+ };
433
+ return [getValue, set];
434
+ }
435
+
436
+ /**
437
+ * Placeholder for content to be rendered.
438
+ * @method render
439
+ */
440
+ render() {}
441
+
442
+ /**
443
+ * Checks if the component is mounted and triggers the onMount method.
444
+ * @private
445
+ */
446
+ checkIFMounted() {
447
+ if (this.mounted) return;
448
+ let timer = setInterval(() => {
449
+ if (document.querySelector('[key="' + this.key + '"]')) {
450
+ clearInterval(timer);
451
+ this.mounted = true;
452
+ this.onMount();
453
+ }
454
+ }, 120);
455
+ }
456
+
457
+ /**
458
+ * Method that is called when the component is mounted.
459
+ * @method onMount
460
+ */
461
+ onMount() {}
462
+ }
463
+
464
+
465
+
466
+
467
+
468
+ let cache = {};
469
+ /**
470
+ * @method require
471
+ * @description Import CommonJS modules like Node.js for the browser
472
+ * @param {string} path
473
+ * @param {Boolean} noresolve - used to tell if the path should be automatically handled or manually handled - this is false by default
474
+ * @returns
475
+ */
476
+ export const require = async (path, noresolve = false) => {
477
+
478
+ if (cache[path]) {
479
+ return cache[path];
480
+ }
481
+ let file = ''
482
+ try {
483
+ file = await fetch(path).then((res) => res.text());
484
+ } catch (error) {
485
+ console.error(error)
486
+ }
487
+
488
+ file = file + `\n//# sourceURL=${path}\n`;
489
+
490
+ let filetype = path.split(".").pop();
491
+ switch (true) {
492
+ case filetype === "js":
493
+ let exports = file.match(/module.exports\s*=\s*{.*}/gs) || file.match(/exports\s*=\s*{.*}/gs);
494
+ exports = exports ? exports[0] : null;
495
+
496
+ if (exports) {
497
+ let keys = exports.split("{")[1].split("}")[0].split(",");
498
+ let returnstring = "";
499
+ keys.forEach((key) => {
500
+ key = key.trim();
501
+ returnstring += `${key},`;
502
+ });
503
+ returnstring = `return {${returnstring}}`;
504
+ file = file += returnstring;
505
+ file = file.replaceAll(exports, "");
506
+ }
507
+
508
+ return new Function(`return (async () => { ${file} })()`)();
509
+ case filetype === "jsx":
510
+ return new Function(`return (async () => { ${file} })()`)()
511
+
512
+ }
513
+ };
514
+
515
+
516
+ window.require = require;
517
+
518
+ /**
519
+ * @method useState - type
520
+ * @param {*} initialState
521
+ * @returns {Array} [value, set]
522
+ * @description Allows you to use state to dynamically update your component
523
+ */
524
+ export const useState = (initialState) => {
525
+ let value = initialState;
526
+ if (key && !states[key]) {
527
+ this.states[key] = initialState;
528
+ }
529
+ return [value, (newValue) => {}];
530
+ };
531
+
532
+ const constants = {};
533
+ let constantCounter = 0;
534
+
535
+ export const constant = (value) => {
536
+ const key = `constant_${constantCounter++}`;
537
+ if (!constants[key]) {
538
+ constants[key] = value;
539
+ }
540
+ return constants[key];
541
+ };
542
+
543
+ export default {
544
+ Component,
545
+ require,
546
+ invoke,
547
+ mem,
548
+ constant,
549
+ useState,
550
+ strictMount,
551
+ stylis,
552
+ }
package/vader.js CHANGED
@@ -741,14 +741,15 @@ Building to ./dist
741
741
  break;
742
742
  default:
743
743
  console.log(`
744
- Vader.js is a reactive framework for building interactive applications for the web built ontop of bun.js!
744
+ Vader.js is a reactive framework for building interactive applications inspired by React.js and Next.js
745
745
 
746
746
  Usage: vader <command>
747
747
 
748
748
  Commands:
749
749
  --watch Watch the pages folder for changes and recompile
750
750
 
751
- --build Build the project
751
+ --build Build the project - use this to initialize the project
752
+
752
753
  Learn more about vader: https://vader-js.pages.dev/
753
754
 
754
755
  `)