vaderjs 1.7.8 → 1.7.9

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/bundler/index.js CHANGED
@@ -93,7 +93,7 @@ const handleReplacements = (code) => {
93
93
 
94
94
  let key = line.split(',')[0].split('[')[1].replace(' ', '');
95
95
 
96
- let updatedLine = line.replace(/\buseState\d*\(/, `this.useState('${key}',`);
96
+ let updatedLine = line.replace(/\buseState\d*\(/, `this.useState('${key + Math.random()}',`);
97
97
 
98
98
  line = updatedLine;
99
99
  }
@@ -103,7 +103,7 @@ const handleReplacements = (code) => {
103
103
 
104
104
  let key = line.split(',')[0].split('[')[1].replace(' ', '');
105
105
 
106
- let updatedLine = line.replace(/\buseAsyncState\d*\(/, `this.useAsyncState('${key}',`);
106
+ let updatedLine = line.replace(/\buseAsyncState\d*\(/, `this.useAsyncState('${key + Math.random()}',`);
107
107
 
108
108
  line = updatedLine;
109
109
  }
@@ -191,7 +191,7 @@ const generatePage = async (
191
191
  import c from '${process.env.filePath}'
192
192
  import {render, e} from '/src/vader/index.js'
193
193
  window.e = e
194
- render(c, document.body.firstChild)
194
+ render(c, document.body)
195
195
  </script>
196
196
  `
197
197
  );
package/index.ts CHANGED
@@ -3,6 +3,8 @@ let isClassComponent = function (element) {
3
3
  return element.toString().startsWith("class");
4
4
  };
5
5
 
6
+
7
+
6
8
  const memoizes = new Map();
7
9
  //@ts-ignore
8
10
 
@@ -98,11 +100,7 @@ export const A = (props: {
98
100
  window.location.reload();
99
101
  return void 0;
100
102
  }
101
- return {
102
- type: "a",
103
- props: { ...props, onClick: handleClick },
104
- children: children || [],
105
- }
103
+ return e("a", { ...props, onClick: handleClick }, props.children);
106
104
  }
107
105
 
108
106
 
@@ -134,22 +132,28 @@ export const e = (element, props, ...children) => {
134
132
  instance.children = children;
135
133
  instance.Mounted = true;
136
134
  return instance.render(props);
137
- case typeof element === "function":
138
- instance = new Component;
139
- instance.render = element;
140
- if(element.name.toLowerCase() == "default"){
141
- throw new Error("Function name must be unique")
142
- }
143
- instance.key = element.name
135
+ case typeof element === "function":
136
+ instance = memoizeClassComponent(Component, element.name);
137
+ element = element.bind(instance);
138
+ instance.render = (props) => element(props);
139
+ if (element.name.toLowerCase() == "default") {
140
+ throw new Error("Function name must be unique");
141
+ }
142
+ instance.key = element.name;
144
143
  instance.Mounted = true;
145
144
  let firstEl = instance.render({ key: instance.key, children, ...props }, children);
146
145
  instance.children = children;
147
146
  if (!firstEl)
148
147
  firstEl = { type: "div", props: { key: instance.key, ...props }, children };
149
148
  firstEl.props = { key: instance.key, ...firstEl.props, ...props };
149
+ firstEl.props["idKey"] = instance.key;
150
150
  return firstEl;
151
151
  default:
152
- return { type: element, props: props || {}, children: children || [] };
152
+ let el = { type: element, props: props || {}, children: children || [] };
153
+ if (el.type !== "head") {
154
+ el.props = { idKey: crypto.randomUUID(), ...el.props };
155
+ }
156
+ return el;
153
157
  }
154
158
  };
155
159
 
@@ -191,10 +195,8 @@ export const useState = (initialState, persist) => {
191
195
  const setState = (newState) => {
192
196
  initialState = newState;
193
197
  };
194
- const getVal = () => {
195
- return initialState;
196
- };
197
- return [getVal, setState];
198
+
199
+ return [initialState, setState];
198
200
  };
199
201
 
200
202
  if (!isServer) {
@@ -243,13 +245,13 @@ export class Component {
243
245
  eventRegistry: any
244
246
  prevState;
245
247
  refs: HTMLElement[] | any[]
246
- state: any[] = []
248
+ state: {}
247
249
  constructor() {
248
250
  this.key = crypto.randomUUID();
249
251
  this.props = {};
250
252
  this.effect = [];
251
253
  this.Mounted = false;
252
- this.state = [];
254
+ this.state = {};
253
255
  this.element = null;
254
256
  this.effectCalls = []
255
257
  this.errorThreshold = 1000
@@ -266,119 +268,91 @@ export class Component {
266
268
  }
267
269
  useEffect(callback, dependencies = []) {
268
270
  const callbackId = callback.toString();
269
-
270
- if (!this.effectCalls.some(s => s.id === callbackId)) {
271
- this.effectCalls.push({ id: callbackId, count: 0, lastCall: Date.now(), runOnce: dependencies.length === 0 });
271
+
272
+ // Initialize effect tracking
273
+ if (!this.effectCalls.some(effect => effect.id === callbackId)) {
274
+ this.effectCalls.push({
275
+ id: callbackId,
276
+ count: 0,
277
+ lastCall: Date.now(),
278
+ runOnce: dependencies.length === 0, // Flag to run only once if no dependencies
279
+ });
272
280
  }
273
-
274
- const effectCall = this.effectCalls.find(s => s.id === callbackId);
275
-
281
+
282
+ const effectCall = this.effectCalls.find(effect => effect.id === callbackId);
283
+
276
284
  const executeCallback = () => {
277
285
  const now = Date.now();
278
286
  const timeSinceLastCall = now - effectCall.lastCall;
279
-
287
+
288
+ // Rate-limiting logic
280
289
  if (timeSinceLastCall < this.errorThreshold) {
281
290
  effectCall.count += 1;
282
291
  if (effectCall.count > this.maxIntervalCalls) {
283
- throw new Error(`Woah wayy too many calls, ensure you are not overlooping you can change the maxThresholdCalls and errorThreshold depending on needs`)
292
+ throw new Error(
293
+ `Woah, way too many calls! Ensure you are not over-looping. Adjust maxIntervalCalls and errorThreshold as needed.`
294
+ );
284
295
  }
285
296
  } else {
286
- effectCall.count = 1;
297
+ effectCall.count = 1; // Reset count for a new interval
287
298
  }
288
-
299
+
289
300
  effectCall.lastCall = now;
290
-
301
+
302
+ // Execute the callback asynchronously
291
303
  setTimeout(() => {
292
304
  try {
293
-
294
- effects.push(callbackId);
295
-
296
- callback()
305
+ effects.push(callbackId); // Track the effect
306
+ callback();
297
307
  } catch (error) {
298
308
  console.error(error);
299
309
  }
300
310
  }, 0);
301
311
  };
302
-
303
- if (dependencies.length === 0 && this.Mounted && this.effect.length === 0 && !effects.includes(callbackId)) {
304
- executeCallback();
305
- this.effect.push(callbackId);
306
- } else {
307
- // Check if dependencies have changed
308
- let dependenciesChanged = false;
309
- if (dependencies.length !== this.effect.length) {
310
- dependenciesChanged = true;
311
- } else {
312
- for (let i = 0; i < dependencies.length; i++) {
313
- if (this.effect[i] !== dependencies[i]) {
314
- dependenciesChanged = true;
315
- break;
316
- }
317
- }
318
- }
319
-
320
- if (dependenciesChanged) {
321
- this.effect = [...dependencies];
312
+
313
+ // Handle empty dependencies: run only once
314
+ if (dependencies.length === 0) {
315
+ if (this.Mounted && !effects.includes(callbackId)) {
322
316
  executeCallback();
317
+ this.effect.push(callbackId);
323
318
  }
319
+ return; // Skip further processing for empty dependencies
324
320
  }
325
- }
326
- useState(key, defaultValue, persist = false) {
327
- if (typeof window === "undefined")
328
- return [defaultValue, () => {}];
329
-
330
- // Retrieve initial value from sessionStorage or defaultValue
331
- let value = sessionStorage.getItem("state_" + key)
332
- ? JSON.parse(sessionStorage.getItem("state_" + key)).value
333
- : defaultValue;
334
-
335
- // Parse stringified values safely
336
- if (typeof value === "string") {
337
- try {
338
- value = JSON.parse(value);
339
- } catch {
340
- // Value remains a string if parsing fails
321
+
322
+ // Check if dependencies have changed
323
+ let dependenciesChanged = false;
324
+ if (dependencies.length !== this.effect.length) {
325
+ dependenciesChanged = true;
326
+ } else {
327
+ for (let i = 0; i < dependencies.length; i++) {
328
+ if (this.effect[i] !== dependencies[i]) {
329
+ dependenciesChanged = true;
330
+ break;
331
+ }
341
332
  }
342
333
  }
343
334
 
344
- // Ensure event listener is added only once
345
- if (!window[`listener_${key}`]) {
346
- window[`listener_${key}`] = true;
347
- window.addEventListener("beforeunload", () => {
348
- if (!persist) sessionStorage.removeItem("state_" + key);
349
- });
335
+ // If dependencies changed, update and execute the callback
336
+ if (dependenciesChanged) {
337
+ this.effect = [...dependencies]; // Update tracked dependencies
338
+ executeCallback();
350
339
  }
340
+ }
351
341
 
342
+ useState(key, defaultValue, persist = false) {
343
+ let value = this.state[key] || defaultValue;
344
+ if (persist) {
345
+ value = sessionStorage.getItem(key) ? JSON.parse(sessionStorage.getItem(key)).value : defaultValue;
346
+ }
352
347
  const setValue = (newValue) => {
353
- if (typeof newValue === "function") {
354
- newValue = newValue(value);
355
- }
356
-
357
- const currentValue = sessionStorage.getItem("state_" + key)
358
- ? JSON.parse(sessionStorage.getItem("state_" + key)).value
359
- : defaultValue;
360
-
361
- if (JSON.stringify(currentValue) === JSON.stringify(newValue)) {
362
- return; // Skip if the value hasn't changed
363
- }
364
-
365
- sessionStorage.setItem(
366
- "state_" + key,
367
- JSON.stringify({ value: newValue })
368
- );
369
-
370
- if (this.forceUpdate) {
371
- this.forceUpdate(this.key);
348
+ this.state[key] = newValue;
349
+ if (persist) {
350
+ sessionStorage.setItem(key, JSON.stringify({ value: newValue }));
372
351
  }
352
+ this.forceUpdate(this.key);
373
353
  };
374
-
375
- const getVal = () => {
376
- return sessionStorage.getItem("state_" + key)
377
- ? JSON.parse(sessionStorage.getItem("state_" + key)).value
378
- : defaultValue;
379
- };
380
-
381
- return [getVal, setValue];
354
+ value = this.state[key] || defaultValue;
355
+ return [value, setValue];
382
356
  }
383
357
  useFetch(url, options) {
384
358
  const loadingKey = "loading_" + url;
@@ -405,18 +379,13 @@ export class Component {
405
379
  return { loading, error, data };
406
380
  }
407
381
  addEventListener(element, event, handler) {
408
- // Ensure element is tracked
409
382
  if (!this.eventRegistry.has(element)) {
410
383
  this.eventRegistry.set(element, []);
411
384
  }
412
-
413
- // Check for duplicates
414
385
  const registeredEvents = this.eventRegistry.get(element);
415
- const isDuplicate = registeredEvents.some(
416
- (e) => e.type === event && e.handler === handler
417
- );
386
+ const isDuplicate = registeredEvents.some((e) => e.type === event && e.handler === handler);
418
387
  if (!isDuplicate) {
419
- element.addEventListener(event, handler);
388
+ element["on" + event] = handler;
420
389
  registeredEvents.push({ type: event, handler });
421
390
  this.eventRegistry.set(element, registeredEvents);
422
391
  }
@@ -430,13 +399,11 @@ export class Component {
430
399
  this.eventRegistry.delete(element);
431
400
  }
432
401
  forceUpdate(key) {
433
- let el = Array.from(document.querySelectorAll("*")).filter((el2) => {
434
- return el2.key === key;
435
- })[0];
436
- let newl = this.toElement();
437
- if (newl.key !== key) {
438
- newl = Array.from(newl.children).filter((el2) => el2.key === key)[0];
439
- }
402
+ let el = document.querySelector(`[idKey="${key}"]`);
403
+ let newl = this.toElement(this.props);
404
+ if (newl.getAttribute("idKey") !== key) {
405
+ newl = Array.from(newl.children).filter((el2) => el2.getAttribute("idKey") === key)[0];
406
+ }
440
407
  this.Reconciler.update(el, newl);
441
408
  }
442
409
  attachEventsRecursively = (element, source) => {
@@ -469,9 +436,22 @@ export class Component {
469
436
  const newChildren = Array.from(newElement.childNodes);
470
437
 
471
438
  const maxLength = Math.max(oldChildren.length, newChildren.length);
439
+ if (oldElement.tagName !== newElement.tagName) {
440
+ const newElementClone = newElement.cloneNode(true);
441
+ oldElement.replaceWith(newElementClone);
442
+
443
+ // Attach events recursively to the new element
444
+ this.attachEventsRecursively(newElementClone, newElement);
445
+ return;
446
+ }
447
+
472
448
  for (let i = 0; i < maxLength; i++) {
473
449
  if (i >= oldChildren.length) {
474
450
  const newChildClone = newChildren[i].cloneNode(true);
451
+ if(oldElement.nodeType === Node.TEXT_NODE) {
452
+ oldElement.textContent = newElement.textContent;
453
+ return;
454
+ }
475
455
  oldElement.appendChild(newChildClone);
476
456
 
477
457
  // Rebind events to the new child (and its children recursively)
@@ -543,7 +523,9 @@ export class Component {
543
523
  // Reapply events for the current element
544
524
  const parentEvents = this.eventRegistry.get(newElement) || [];
545
525
  parentEvents.forEach(({ type, handler }) => {
546
- this.addEventListener(oldElement, type, handler);
526
+ if (newElement.nodeType === oldElement.nodeType) {
527
+ this.addEventListener(oldElement, type, handler);
528
+ }
547
529
  });
548
530
 
549
531
  },
@@ -601,6 +583,9 @@ export class Component {
601
583
  // Set attributes
602
584
  let attributes = element.props || {};
603
585
  for (let key in attributes) {
586
+ if(typeof attributes[key] === "object" && key !== "style"){
587
+ continue;
588
+ }
604
589
  if (key === "key") {
605
590
  el.key = attributes[key];
606
591
  } else if (key === "className") {
@@ -632,7 +617,7 @@ export class Component {
632
617
  child.forEach((nestedChild) => el.appendChild(this.parseToElement(nestedChild)));
633
618
  } else if (typeof child === "function") {
634
619
  // Handle functional components
635
- let component = memoizeClassComponent(Component);
620
+ let component = memoizeClassComponent(Component, child.name);
636
621
  component.Mounted = true;
637
622
  component.render = child;
638
623
  let componentElement = component.toElement();
@@ -655,10 +640,9 @@ export class Component {
655
640
  return { type: element, props: props || {}, children: children || [] };
656
641
  }
657
642
  toElement() {
658
- let children = this.render();
659
-
643
+ let children = this.render(this.props);
660
644
  let el = this.parseToElement(children);
661
- el.key = this.key;
645
+ el.setAttribute("idKey", this.key);
662
646
  return el;
663
647
  }
664
648
  render() {
@@ -666,15 +650,13 @@ export class Component {
666
650
  }
667
651
  }
668
652
 
669
- function memoizeClassComponent(Component: any) {
670
- let key = Component.toString();
671
- if (memoizes.has(key)) {
672
- return memoizes.get(key);
653
+ function memoizeClassComponent(Component, key) {
654
+ let instance = memoizes.get(key);
655
+ if (!instance) {
656
+ instance = new Component(key);
657
+ memoizes.set(key, instance);
673
658
  }
674
- let instance = new Component();
675
- memoizes.set(key, instance);
676
659
  return instance;
677
-
678
660
  }
679
661
  /**
680
662
  * @description - Render jsx Componenet to the DOM
@@ -682,6 +664,17 @@ function memoizeClassComponent(Component: any) {
682
664
  * @param container
683
665
  */
684
666
  export function render(element, container) {
667
+ // CLEAR STATE ON RELOAD
668
+ if (!isServer) {
669
+ window.addEventListener("beforeunload", () => {
670
+ let keys = Object.keys(sessionStorage);
671
+ keys.forEach((key) => {
672
+ if (key.startsWith("state_")) {
673
+ sessionStorage.removeItem(key);
674
+ }
675
+ });
676
+ });
677
+ }
685
678
  if (isClassComponent(element)) {
686
679
  const instance = new element;
687
680
  instance.Mounted = true;
@@ -690,16 +683,17 @@ export function render(element, container) {
690
683
  container.innerHTML = "";
691
684
  container.replaceWith(el);
692
685
  } else {
693
- let memoizedInstance = memoizeClassComponent(Component);
686
+ let memoizedInstance = memoizeClassComponent(Component, element.name);
694
687
  memoizedInstance.Mounted = true;
695
- memoizedInstance.render = element.bind(memoizedInstance);
696
- if(element.name == "default"){
697
- throw new Error("Function name Must be a unique function name as it is used for a element key")
688
+ element = element.bind(memoizedInstance);
689
+ memoizedInstance.render = (props) => element(props);
690
+ if (element.name == "default") {
691
+ throw new Error("Function name Must be a unique function name as it is used for a element key");
698
692
  }
699
- memoizedInstance.key = element.name
700
- let el = memoizedInstance.toElement();
701
- el.key = element.name
693
+ memoizedInstance.key = element.name;
694
+ let el = memoizedInstance.toElement();
695
+ el.key = element.name;
702
696
  container.innerHTML = "";
703
697
  container.replaceWith(el);
704
698
  }
705
- }
699
+ }
package/main.js CHANGED
@@ -200,7 +200,7 @@ async function generateApp() {
200
200
  Object.keys(routes.routes).forEach(async (route) => {
201
201
 
202
202
  let r = routes.routes[route]
203
- let code = await Bun.file(r).text()
203
+ let code = await Bun.file(r).text()
204
204
  let size = code.length / 1024
205
205
  r = r.replace(process.cwd().replace(/\\/g, '/') + '/app', '')
206
206
  var beforeR = r
@@ -227,7 +227,7 @@ async function generateApp() {
227
227
  fs.writeFileSync(process.cwd() + '/dist/src/vader/index.js', await new Bun.Transpiler({
228
228
  loader: 'ts',
229
229
  }).transformSync(await Bun.file(require.resolve('vaderjs')).text()))
230
-
230
+
231
231
  Bun.spawn({
232
232
  cmd: ['bun', 'run', './dev/bundler.js'],
233
233
  cwd: process.cwd(),
@@ -242,8 +242,8 @@ async function generateApp() {
242
242
  bindes: bindes.join('\n'),
243
243
  isTs: beforeR.endsWith(".tsx"),
244
244
  filePath: r,
245
-
246
- isJsx: beforeR.endsWith('.tsx') || beforeR.endsWith(".jsx") ,
245
+
246
+ isJsx: beforeR.endsWith('.tsx') || beforeR.endsWith(".jsx"),
247
247
  isAppFile: true,
248
248
  INPUT: `../app/${beforeR}`,
249
249
  },
@@ -304,7 +304,7 @@ async function generateApp() {
304
304
 
305
305
  function handleFiles() {
306
306
  return new Promise(async (resolve, reject) => {
307
- try {
307
+ try {
308
308
  let glob = new Glob('public/**/*')
309
309
  for await (var i of glob.scan()) {
310
310
  let file = i
@@ -323,9 +323,9 @@ function handleFiles() {
323
323
  let code = await Bun.file(file).text()
324
324
 
325
325
  code = handleReplacements(code)
326
- var url = file
326
+ var url = file
327
327
  file = file.replace('.jsx', '.js').replace('.tsx', '.js')
328
- fs.writeFileSync(path.join(process.cwd() + '/dist', file.replace('.jsx', '.js').replace('.tsx', '.js')), code)
328
+ fs.writeFileSync(path.join(process.cwd() + '/dist', file.replace('.jsx', '.js').replace('.tsx', '.js')), code)
329
329
  await Bun.spawn({
330
330
  cmd: ['bun', 'run', './dev/bundler.js'],
331
331
  cwd: process.cwd(),
@@ -339,7 +339,7 @@ function handleFiles() {
339
339
  DEV: mode === 'development',
340
340
  size: code.length / 1024,
341
341
  filePath: file.replace('.jsx', '.js'),
342
- isJsx: url.endsWith('.tsx') || url.endsWith(".jsx") ,
342
+ isJsx: url.endsWith('.tsx') || url.endsWith(".jsx"),
343
343
  isAppFile: false,
344
344
  INPUT: path.join(process.cwd(), url),
345
345
  },
@@ -391,9 +391,9 @@ function handleFiles() {
391
391
  }
392
392
  globalThis.clients = []
393
393
 
394
- if (mode === 'development') {
394
+ if (mode === 'development') {
395
395
  await handleFiles()
396
- await generateApp()
396
+ await generateApp()
397
397
  const watcher = fs.watch(path.join(process.cwd() + '/'), { recursive: true })
398
398
  let isBuilding = false; // Flag to track build status
399
399
 
@@ -403,10 +403,10 @@ if (mode === 'development') {
403
403
  // Function to handle file changes with debounce
404
404
  const handleFileChangeDebounced = async (change, file) => {
405
405
  if (file.endsWith('.tsx') || file.endsWith('.jsx') || file.endsWith('.css') || file.endsWith('.ts')
406
- && !file.includes('node_module')
406
+ && !file.includes('node_module')
407
407
  ) {
408
408
  // delete files cache
409
- if (file.endsWith('vader.config.ts')){
409
+ if (file.endsWith('vader.config.ts')) {
410
410
  delete require.cache[require.resolve(process.cwd() + '/vader.config.ts')]
411
411
 
412
412
  config = require(process.cwd() + '/vader.config.ts').default
@@ -494,16 +494,16 @@ if (mode == 'development' || mode == 'serve') {
494
494
  style: 'nextjs'
495
495
  })
496
496
  router.reload()
497
- let route = router.match(url.pathname)
497
+ let route = router.match(url.pathname)
498
498
  if (!route) {
499
499
  return new Response('Not found', { status: 404 })
500
500
  }
501
501
  let p = route.pathname;
502
502
  let base = path.dirname(route.filePath)
503
503
  base = base.replace(/\\/g, '/')
504
- base = base.replace(path.join(process.cwd() + '/app').replace(/\\/g, '/'), '')
505
- base = base.replace(/\\/g, '/').replace('/app', '/dist')
506
- base = process.cwd() + "/dist/" + base
504
+ base = base.replace(path.join(process.cwd() + '/app').replace(/\\/g, '/'), '');
505
+ base = base.replace(/\\/g, '/').replace(/\/app(\/|$)/, '/dist$1');
506
+ base = process.cwd() + '/dist/' + base;
507
507
  let data = await Bun.file(path.join(base, 'index.html')).text()
508
508
  if (mode == "development") {
509
509
  return new Response(data + `
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vaderjs",
3
- "version": "1.7.8",
3
+ "version": "1.7.9",
4
4
  "description": "A simple and powerful JavaScript library for building modern web applications.",
5
5
  "bin": {
6
6
  "vaderjs": "./main.js"