@geektech/tsone 0.0.1 → 0.0.2

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.
@@ -1,10 +1,11 @@
1
1
  import {
2
2
  StyleManager
3
- } from "./index-dgv88dz4.js";
3
+ } from "./index-8wjswsye.js";
4
4
 
5
5
  // lib/core/reactive/types.ts
6
6
  var IS_REACTIVE = Symbol("is_reactive");
7
7
  var IS_READONLY = Symbol("is_readonly");
8
+ var IS_REF = Symbol("is_ref");
8
9
  var MUTATING_ARRAY_METHODS = [
9
10
  "push",
10
11
  "pop",
@@ -62,7 +63,7 @@ class ReactiveSystem {
62
63
  }
63
64
  this.track(target2, key);
64
65
  const value = Reflect.get(target2, key);
65
- if (value && typeof value === "object" && !Array.isArray(value)) {
66
+ if (isObject(value) && !hasReactiveFlag(value, IS_READONLY)) {
66
67
  return this.reactive(value);
67
68
  }
68
69
  return value;
@@ -73,7 +74,7 @@ class ReactiveSystem {
73
74
  return false;
74
75
  }
75
76
  const oldValue = Reflect.get(target2, key);
76
- if (value && typeof value === "object" && !Array.isArray(value) && !hasReactiveFlag(value, IS_REACTIVE)) {
77
+ if (isObject(value) && !hasReactiveFlag(value, IS_REACTIVE) && !hasReactiveFlag(value, IS_READONLY)) {
77
78
  value = this.reactive(value);
78
79
  }
79
80
  const result = Reflect.set(target2, key, value);
@@ -121,6 +122,9 @@ class ReactiveSystem {
121
122
  return result;
122
123
  };
123
124
  }
125
+ if (isObject(value) && !hasReactiveFlag(value, IS_READONLY)) {
126
+ return this.reactive(value);
127
+ }
124
128
  return value;
125
129
  },
126
130
  set: (target2, key, value) => {
@@ -129,7 +133,7 @@ class ReactiveSystem {
129
133
  return false;
130
134
  }
131
135
  const oldValue = Reflect.get(target2, key);
132
- if (value && typeof value === "object" && !Array.isArray(value) && !hasReactiveFlag(value, IS_REACTIVE)) {
136
+ if (isObject(value) && !hasReactiveFlag(value, IS_REACTIVE) && !hasReactiveFlag(value, IS_READONLY)) {
133
137
  value = this.reactive(value);
134
138
  }
135
139
  const result = Reflect.set(target2, key, value);
@@ -171,11 +175,14 @@ class ReactiveSystem {
171
175
  }
172
176
  const proxy = new Proxy(target, {
173
177
  get: (target2, key) => {
174
- if (key === IS_REACTIVE || key === IS_READONLY) {
178
+ if (key === IS_REACTIVE) {
179
+ return false;
180
+ }
181
+ if (key === IS_READONLY) {
175
182
  return true;
176
183
  }
177
184
  const value = Reflect.get(target2, key);
178
- if (value && typeof value === "object" && !Array.isArray(value)) {
185
+ if (isObject(value)) {
179
186
  return this.readonly(value);
180
187
  }
181
188
  return value;
@@ -193,7 +200,7 @@ class ReactiveSystem {
193
200
  return proxy;
194
201
  }
195
202
  effect(fn, options) {
196
- const { lazy = false, scheduler } = options || {};
203
+ const { lazy = false, scheduler, throwOnError = false } = options || {};
197
204
  const effectFn = () => {
198
205
  if (!effectFn.active) {
199
206
  return fn();
@@ -204,6 +211,9 @@ class ReactiveSystem {
204
211
  this.activeEffect = effectFn;
205
212
  return fn();
206
213
  } catch (error) {
214
+ if (throwOnError) {
215
+ throw error;
216
+ }
207
217
  console.error("Effect error:", error);
208
218
  return;
209
219
  } finally {
@@ -310,6 +320,21 @@ function effect(fn, options) {
310
320
  function computed(getter) {
311
321
  return ReactiveSystem.getInstance().computed(getter);
312
322
  }
323
+ function ref(value) {
324
+ const wrapper = { value };
325
+ Object.defineProperty(wrapper, IS_REF, {
326
+ configurable: false,
327
+ enumerable: false,
328
+ value: true
329
+ });
330
+ return reactive(wrapper);
331
+ }
332
+ function isRef(value) {
333
+ return isObject(value) && Boolean(Reflect.get(value, IS_REF));
334
+ }
335
+ function unref(value) {
336
+ return isRef(value) ? value.value : value;
337
+ }
313
338
  function stop(effect2) {
314
339
  ReactiveSystem.getInstance().stop(effect2);
315
340
  }
@@ -354,6 +379,187 @@ function setStyleValue(style, property, value) {
354
379
  style.setProperty(cssProperty, String(value));
355
380
  }
356
381
 
382
+ // lib/core/model.ts
383
+ function pathSegments(path) {
384
+ const segments = path.split(".");
385
+ if (path.length === 0 || segments.some((segment) => segment.length === 0 || segment === "__proto__" || segment === "prototype" || segment === "constructor")) {
386
+ throw new Error(`Invalid model path "${path}"`);
387
+ }
388
+ return segments;
389
+ }
390
+ function isRecord(value) {
391
+ return typeof value === "object" && value !== null && !Array.isArray(value);
392
+ }
393
+ function hasOwn(value, key) {
394
+ return Object.prototype.hasOwnProperty.call(value, key);
395
+ }
396
+ function modelPath(binding) {
397
+ return typeof binding === "string" ? binding : binding.path;
398
+ }
399
+ function getModelValue(state, path) {
400
+ let value = state;
401
+ for (const segment of pathSegments(path)) {
402
+ if (!isRecord(value)) {
403
+ throw new Error(`Invalid model path "${path}"`);
404
+ }
405
+ if (!hasOwn(value, segment)) {
406
+ if (segment in value) {
407
+ throw new Error(`Invalid model path "${path}"`);
408
+ }
409
+ return;
410
+ }
411
+ value = value[segment];
412
+ }
413
+ return value;
414
+ }
415
+ function setModelValue(state, path, value) {
416
+ const segments = pathSegments(path);
417
+ let target = state;
418
+ for (const segment of segments.slice(0, -1)) {
419
+ if (!hasOwn(target, segment)) {
420
+ if (segment in target) {
421
+ throw new Error(`Invalid model path "${path}"`);
422
+ }
423
+ target[segment] = {};
424
+ } else if (!isRecord(target[segment])) {
425
+ throw new Error(`Invalid model path "${path}"`);
426
+ }
427
+ const nextTarget = target[segment];
428
+ if (!isRecord(nextTarget)) {
429
+ throw new Error(`Invalid model path "${path}"`);
430
+ }
431
+ target = nextTarget;
432
+ }
433
+ const lastSegment = segments[segments.length - 1];
434
+ target[lastSegment] = value;
435
+ }
436
+ function displayValue(binding, value) {
437
+ if (typeof binding !== "string" && binding.format) {
438
+ return binding.format(value);
439
+ }
440
+ return value === undefined || value === null ? "" : String(value);
441
+ }
442
+ function toModelValue(binding, value) {
443
+ if (typeof binding !== "string" && binding.parse) {
444
+ return binding.parse(value);
445
+ }
446
+ return value;
447
+ }
448
+ function syncControl(element, binding, value) {
449
+ if (element instanceof HTMLInputElement) {
450
+ if (element.type === "checkbox") {
451
+ element.checked = Array.isArray(value) ? value.some((item) => String(item) === element.value) : Boolean(value);
452
+ return;
453
+ }
454
+ if (element.type === "radio") {
455
+ element.checked = value === element.value;
456
+ return;
457
+ }
458
+ element.value = displayValue(binding, value);
459
+ return;
460
+ }
461
+ if (element instanceof HTMLTextAreaElement) {
462
+ element.value = displayValue(binding, value);
463
+ return;
464
+ }
465
+ if (element instanceof HTMLSelectElement) {
466
+ if (element.multiple) {
467
+ const selected = Array.isArray(value) ? new Set(value.map(String)) : new Set;
468
+ for (let index = 0;index < element.options.length; index += 1) {
469
+ const option = element.options.item(index);
470
+ if (!option) {
471
+ continue;
472
+ }
473
+ option.selected = selected.has(option.value);
474
+ }
475
+ return;
476
+ }
477
+ element.value = displayValue(binding, value);
478
+ }
479
+ }
480
+ function controlValue(element, currentValue) {
481
+ if (element instanceof HTMLInputElement) {
482
+ if (element.type === "checkbox") {
483
+ if (Array.isArray(currentValue)) {
484
+ const values = currentValue.filter((value) => String(value) !== element.value);
485
+ return element.checked ? [...values, element.value] : values;
486
+ }
487
+ return element.checked;
488
+ }
489
+ if (element.type === "radio") {
490
+ return element.checked ? element.value : currentValue;
491
+ }
492
+ return element.value;
493
+ }
494
+ if (element instanceof HTMLTextAreaElement) {
495
+ return element.value;
496
+ }
497
+ if (element instanceof HTMLSelectElement) {
498
+ if (!element.multiple) {
499
+ return element.value;
500
+ }
501
+ const values = [];
502
+ for (let index = 0;index < element.selectedOptions.length; index += 1) {
503
+ const option = element.selectedOptions.item(index);
504
+ if (option) {
505
+ values.push(option.value);
506
+ }
507
+ }
508
+ return values;
509
+ }
510
+ return;
511
+ }
512
+
513
+ class ModelBindingController {
514
+ bindings = new WeakMap;
515
+ bind(element, binding, state) {
516
+ if (!this.isSupportedControl(element)) {
517
+ return;
518
+ }
519
+ const existing = this.bindings.get(element);
520
+ if (existing && this.sameBinding(existing, binding)) {
521
+ return;
522
+ }
523
+ this.cleanup(element);
524
+ const path = modelPath(binding);
525
+ const sync = () => syncControl(element, binding, getModelValue(state, path));
526
+ const eventName = element instanceof HTMLTextAreaElement || element instanceof HTMLInputElement && !["checkbox", "radio"].includes(element.type) ? "input" : "change";
527
+ const listener = () => {
528
+ const currentValue = getModelValue(state, path);
529
+ setModelValue(state, path, toModelValue(binding, controlValue(element, currentValue)));
530
+ };
531
+ element.addEventListener(eventName, listener);
532
+ const effectRef = effect(sync);
533
+ this.bindings.set(element, {
534
+ binding,
535
+ path,
536
+ parse: typeof binding === "string" ? undefined : binding.parse,
537
+ format: typeof binding === "string" ? undefined : binding.format,
538
+ eventName,
539
+ listener,
540
+ effect: effectRef
541
+ });
542
+ }
543
+ cleanup(element) {
544
+ const existing = this.bindings.get(element);
545
+ if (!existing) {
546
+ return;
547
+ }
548
+ element.removeEventListener(existing.eventName, existing.listener);
549
+ stop(existing.effect);
550
+ this.bindings.delete(element);
551
+ }
552
+ isSupportedControl(element) {
553
+ return element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement;
554
+ }
555
+ sameBinding(record, binding) {
556
+ if (typeof record.binding === "string" || typeof binding === "string") {
557
+ return record.binding === binding;
558
+ }
559
+ return record.path === binding.path && record.parse === binding.parse && record.format === binding.format;
560
+ }
561
+ }
562
+
357
563
  // lib/core/vnode.ts
358
564
  function isComponentNode(vnode) {
359
565
  return typeof vnode === "object" && vnode !== null && "component" in vnode;
@@ -374,6 +580,17 @@ function h(tag, props, children, listeners, key, directions) {
374
580
  directions
375
581
  };
376
582
  }
583
+ function createElementFactory(tag) {
584
+ return (options = {}) => ({
585
+ tag,
586
+ ...options
587
+ });
588
+ }
589
+ var Div = createElementFactory("div");
590
+ var Span = createElementFactory("span");
591
+ var P = createElementFactory("p");
592
+ var Button = createElementFactory("button");
593
+ var Input = createElementFactory("input");
377
594
  function createComponent(componentClass, props, children, key, directions) {
378
595
  return {
379
596
  component: componentClass,
@@ -391,6 +608,15 @@ function slot(name, key, directions) {
391
608
  directions
392
609
  };
393
610
  }
611
+ function each(items, render, key) {
612
+ return items.map((item, index) => {
613
+ const vnode = render(item, index);
614
+ if (typeof vnode === "string") {
615
+ throw new Error("each render callback must return a VNode");
616
+ }
617
+ return { ...vnode, key: key(item, index) };
618
+ });
619
+ }
394
620
 
395
621
  // lib/core/renderer.ts
396
622
  class RendererContext {
@@ -449,44 +675,62 @@ class TextRenderStrategy {
449
675
 
450
676
  class ComponentRenderStrategy {
451
677
  instances = new WeakMap;
678
+ instanceNodes = new Map;
679
+ emitterUnsubscribers = new WeakMap;
452
680
  matches(vnode) {
453
681
  return typeof vnode === "object" && vnode !== null && isComponentNode(vnode);
454
682
  }
455
683
  mount(vnode, context) {
684
+ if (vnode.directions?.if === false) {
685
+ return document.createComment("if");
686
+ }
456
687
  const ComponentClass = vnode.component;
457
688
  const instance = new ComponentClass(this.createProps(vnode));
458
689
  if (context.appContext && instance.setAppContext) {
459
690
  instance.setAppContext(context.appContext);
460
691
  }
461
- if (vnode.emitters) {
462
- Object.entries(vnode.emitters).forEach(([eventName, listener]) => {
463
- instance.on(eventName, listener);
464
- });
465
- }
692
+ this.syncEmitters(instance, vnode.emitters ?? {});
466
693
  context.registerChild(instance);
467
694
  const node = instance.mountToNode();
468
- this.instances.set(node, instance);
695
+ this.trackInstanceNode(instance, node);
696
+ instance.setElementChangeListener?.((previousNode, nextNode) => {
697
+ this.trackInstanceNode(instance, previousNode);
698
+ this.trackInstanceNode(instance, nextNode);
699
+ });
469
700
  return node;
470
701
  }
471
702
  patch(oldVNode, newVNode, currentNode, context) {
703
+ if (currentNode.nodeType === Node.COMMENT_NODE) {
704
+ const nextNode2 = this.mount(newVNode, context);
705
+ currentNode.parentNode?.replaceChild(nextNode2, currentNode);
706
+ return nextNode2;
707
+ }
708
+ if (newVNode.directions?.if === false) {
709
+ const nextNode2 = document.createComment("if");
710
+ currentNode.parentNode?.replaceChild(nextNode2, currentNode);
711
+ this.unmount(oldVNode, currentNode, context);
712
+ return nextNode2;
713
+ }
472
714
  const instance = this.instances.get(currentNode);
473
715
  if (instance && oldVNode.component === newVNode.component) {
716
+ this.syncEmitters(instance, newVNode.emitters ?? {});
474
717
  instance.setProps(this.createProps(newVNode));
475
- instance.update();
476
718
  const nextNode2 = instance.getElement() ?? currentNode;
477
- this.instances.set(nextNode2, instance);
719
+ this.trackInstanceNode(instance, nextNode2);
478
720
  return nextNode2;
479
721
  }
480
722
  const nextNode = this.mount(newVNode, context);
481
723
  currentNode.parentNode?.replaceChild(nextNode, currentNode);
482
- this.unmount(oldVNode, currentNode);
724
+ this.unmount(oldVNode, currentNode, context);
483
725
  return nextNode;
484
726
  }
485
- unmount(_vnode, currentNode) {
727
+ unmount(_vnode, currentNode, context) {
486
728
  const instance = this.instances.get(currentNode);
487
729
  if (instance) {
730
+ this.clearEmitters(instance);
488
731
  instance.unmount();
489
- this.instances.delete(currentNode);
732
+ this.clearInstanceNodes(instance);
733
+ context.unregisterChild(instance);
490
734
  }
491
735
  }
492
736
  createProps(vnode) {
@@ -495,6 +739,44 @@ class ComponentRenderStrategy {
495
739
  children: vnode.children ?? []
496
740
  };
497
741
  }
742
+ syncEmitters(instance, emitters) {
743
+ const current = this.emitterUnsubscribers.get(instance) ?? new Map;
744
+ current.forEach(({ listener: currentListener, unsubscribe }, eventName) => {
745
+ const listener = emitters[eventName];
746
+ if (!listener || listener !== currentListener) {
747
+ unsubscribe();
748
+ current.delete(eventName);
749
+ }
750
+ });
751
+ Object.entries(emitters).forEach(([eventName, listener]) => {
752
+ if (current.get(eventName)?.listener === listener) {
753
+ return;
754
+ }
755
+ current.set(eventName, {
756
+ listener,
757
+ unsubscribe: instance.on(eventName, listener)
758
+ });
759
+ });
760
+ this.emitterUnsubscribers.set(instance, current);
761
+ }
762
+ clearEmitters(instance) {
763
+ this.emitterUnsubscribers.get(instance)?.forEach(({ unsubscribe }) => {
764
+ unsubscribe();
765
+ });
766
+ this.emitterUnsubscribers.delete(instance);
767
+ }
768
+ trackInstanceNode(instance, node) {
769
+ this.instances.set(node, instance);
770
+ const nodes = this.instanceNodes.get(instance) ?? new Set;
771
+ nodes.add(node);
772
+ this.instanceNodes.set(instance, nodes);
773
+ }
774
+ clearInstanceNodes(instance) {
775
+ this.instanceNodes.get(instance)?.forEach((node) => {
776
+ this.instances.delete(node);
777
+ });
778
+ this.instanceNodes.delete(instance);
779
+ }
498
780
  }
499
781
 
500
782
  class SlotRenderStrategy {
@@ -503,12 +785,26 @@ class SlotRenderStrategy {
503
785
  return typeof vnode === "object" && vnode !== null && isSlotProvider(vnode);
504
786
  }
505
787
  mount(vnode, context) {
788
+ if (vnode.directions?.if === false) {
789
+ return document.createComment("if");
790
+ }
506
791
  const slotContainer = document.createElement("div");
507
792
  slotContainer.setAttribute("data-slot", vnode.props.name);
508
793
  this.mountSlotChildren(slotContainer, this.resolveChildren(vnode, context), context);
509
794
  return slotContainer;
510
795
  }
511
796
  patch(oldVNode, newVNode, currentNode, context) {
797
+ if (currentNode.nodeType === Node.COMMENT_NODE) {
798
+ const nextNode = this.mount(newVNode, context);
799
+ currentNode.parentNode?.replaceChild(nextNode, currentNode);
800
+ return nextNode;
801
+ }
802
+ if (newVNode.directions?.if === false) {
803
+ const nextNode = document.createComment("if");
804
+ currentNode.parentNode?.replaceChild(nextNode, currentNode);
805
+ this.unmount(oldVNode, currentNode, context);
806
+ return nextNode;
807
+ }
512
808
  if (currentNode instanceof HTMLElement) {
513
809
  currentNode.setAttribute("data-slot", newVNode.props.name);
514
810
  this.replaceSlotChildren(currentNode, oldVNode, newVNode, context);
@@ -550,7 +846,7 @@ class SlotRenderStrategy {
550
846
  class ElementRenderStrategy {
551
847
  listeners = new WeakMap;
552
848
  effects = new WeakMap;
553
- modelBindings = new WeakMap;
849
+ modelBindings = new ModelBindingController;
554
850
  matches(vnode) {
555
851
  return typeof vnode === "object" && vnode !== null && isHTMLNode(vnode);
556
852
  }
@@ -560,11 +856,11 @@ class ElementRenderStrategy {
560
856
  }
561
857
  const element = document.createElement(vnode.tag);
562
858
  this.applyProps(element, {}, vnode.props ?? {}, context);
563
- this.applyDirections(element, undefined, vnode.directions, context);
564
859
  this.updateListeners(element, {}, this.collectListeners(vnode));
565
860
  (vnode.children ?? []).forEach((child) => {
566
861
  element.appendChild(context.renderer.mount(child, context));
567
862
  });
863
+ this.applyDirections(element, undefined, vnode.directions, context);
568
864
  return element;
569
865
  }
570
866
  patch(oldVNode, newVNode, currentNode, context) {
@@ -584,9 +880,9 @@ class ElementRenderStrategy {
584
880
  return nextNode;
585
881
  }
586
882
  this.applyProps(currentNode, oldVNode.props ?? {}, newVNode.props ?? {}, context);
587
- this.applyDirections(currentNode, oldVNode.directions, newVNode.directions, context);
588
883
  this.updateListeners(currentNode, this.collectListeners(oldVNode), this.collectListeners(newVNode));
589
884
  this.updateChildren(currentNode, oldVNode.children ?? [], newVNode.children ?? [], context);
885
+ this.applyDirections(currentNode, oldVNode.directions, newVNode.directions, context);
590
886
  return currentNode;
591
887
  }
592
888
  unmount(vnode, currentNode, context) {
@@ -599,7 +895,7 @@ class ElementRenderStrategy {
599
895
  currentNode.removeEventListener(eventName, listener);
600
896
  });
601
897
  this.listeners.delete(currentNode);
602
- this.modelBindings.delete(currentNode);
898
+ this.modelBindings.cleanup(currentNode);
603
899
  (vnode.children ?? []).forEach((child, index) => {
604
900
  const childNode = currentNode.childNodes[index];
605
901
  if (childNode) {
@@ -656,12 +952,16 @@ class ElementRenderStrategy {
656
952
  } else if (oldDirections && "show" in oldDirections) {
657
953
  element.style.display = "";
658
954
  }
659
- if (newDirections?.model) {
660
- this.setupTwoWayBinding(element, newDirections.model, context);
955
+ if (!newDirections?.model) {
956
+ this.modelBindings.cleanup(element);
957
+ return;
661
958
  }
959
+ this.modelBindings.bind(element, newDirections.model, context.templateEngine.state);
662
960
  }
663
961
  updateChildren(element, oldChildren, newChildren, context) {
664
- if (this.hasKeyedChildren(oldChildren, newChildren)) {
962
+ this.assertNoDuplicateKeys(oldChildren);
963
+ this.assertNoDuplicateKeys(newChildren);
964
+ if (this.hasOnlyKeyedChildren(oldChildren, newChildren)) {
665
965
  this.updateKeyedChildren(element, oldChildren, newChildren, context);
666
966
  return;
667
967
  }
@@ -730,8 +1030,21 @@ class ElementRenderStrategy {
730
1030
  }
731
1031
  });
732
1032
  }
733
- hasKeyedChildren(oldChildren, newChildren) {
734
- return [...oldChildren, ...newChildren].some((child) => this.getVNodeKey(child) !== undefined);
1033
+ hasOnlyKeyedChildren(oldChildren, newChildren) {
1034
+ return [...oldChildren, ...newChildren].every((child) => this.getVNodeKey(child) !== undefined);
1035
+ }
1036
+ assertNoDuplicateKeys(children) {
1037
+ const keys = new Set;
1038
+ children.forEach((child) => {
1039
+ const key = this.getVNodeKey(child);
1040
+ if (key === undefined) {
1041
+ return;
1042
+ }
1043
+ if (keys.has(key)) {
1044
+ throw new Error(`Duplicate key "${key}"`);
1045
+ }
1046
+ keys.add(key);
1047
+ });
735
1048
  }
736
1049
  getVNodeKey(vnode) {
737
1050
  if (typeof vnode === "string") {
@@ -780,55 +1093,6 @@ class ElementRenderStrategy {
780
1093
  });
781
1094
  this.trackEffect(element, effectRef);
782
1095
  }
783
- setupTwoWayBinding(element, modelKey, context) {
784
- if (!(element instanceof HTMLInputElement) && !(element instanceof HTMLTextAreaElement) && !(element instanceof HTMLSelectElement)) {
785
- return;
786
- }
787
- if (this.modelBindings.get(element) === modelKey) {
788
- return;
789
- }
790
- this.modelBindings.set(element, modelKey);
791
- const getValue = () => {
792
- const value = this.getStateValue(context, modelKey);
793
- return value === undefined || value === null ? "" : String(value);
794
- };
795
- const setValue = (value) => {
796
- const keys = modelKey.split(".");
797
- let target = context.templateEngine.state;
798
- for (let index = 0;index < keys.length - 1; index += 1) {
799
- const key = keys[index];
800
- if (!target[key] || typeof target[key] !== "object") {
801
- target[key] = {};
802
- }
803
- target = target[key];
804
- }
805
- target[keys[keys.length - 1]] = value;
806
- };
807
- element.value = getValue();
808
- const eventName = element instanceof HTMLSelectElement ? "change" : "input";
809
- const inputListener = () => {
810
- setValue(element.value);
811
- };
812
- element.addEventListener(eventName, inputListener);
813
- const store = this.listeners.get(element) ?? new Map;
814
- store.set(`model:${modelKey}`, { eventName, listener: inputListener });
815
- this.listeners.set(element, store);
816
- const effectRef = effect(() => {
817
- const nextValue = getValue();
818
- if (element.value !== nextValue) {
819
- element.value = nextValue;
820
- }
821
- });
822
- this.trackEffect(element, effectRef);
823
- }
824
- getStateValue(context, modelKey) {
825
- return modelKey.split(".").reduce((value, key) => {
826
- if (!value || typeof value !== "object") {
827
- return;
828
- }
829
- return value[key];
830
- }, context.templateEngine.state);
831
- }
832
1096
  trackEffect(element, effectRef) {
833
1097
  const effects = this.effects.get(element) ?? new Set;
834
1098
  effects.add(effectRef);
@@ -889,8 +1153,8 @@ class TemplateEngine {
889
1153
  if (key) {
890
1154
  keys.add(key);
891
1155
  const value = this.getValueFromState(key);
892
- const displayValue = value === undefined || value === null ? "" : String(value);
893
- result = result.replace(match[0], displayValue);
1156
+ const displayValue2 = value === undefined || value === null ? "" : String(value);
1157
+ result = result.replace(match[0], displayValue2);
894
1158
  }
895
1159
  });
896
1160
  return result;
@@ -910,7 +1174,7 @@ class TemplateEngine {
910
1174
  }
911
1175
  clearBindings() {
912
1176
  this.bindings.forEach((binding) => {
913
- binding.effect.active = false;
1177
+ stop(binding.effect);
914
1178
  });
915
1179
  this.bindings = [];
916
1180
  }
@@ -953,8 +1217,11 @@ class Component {
953
1217
  templateEngine;
954
1218
  childComponents = new Set;
955
1219
  eventListeners = {};
1220
+ providers = new Map;
956
1221
  updateEffect;
957
1222
  appContext = null;
1223
+ parentComponent = null;
1224
+ elementChangeListener = null;
958
1225
  styleManager;
959
1226
  state;
960
1227
  mounted = false;
@@ -969,7 +1236,7 @@ class Component {
969
1236
  if (this.mounted) {
970
1237
  this.update();
971
1238
  }
972
- });
1239
+ }, { throwOnError: true });
973
1240
  }
974
1241
  mount(container) {
975
1242
  if (!container || !(container instanceof HTMLElement)) {
@@ -997,15 +1264,15 @@ class Component {
997
1264
  if (!this.el || !this.vnode) {
998
1265
  return;
999
1266
  }
1000
- try {
1001
- this.beforeUpdate();
1002
- const newVNode = this.render();
1003
- this.el = this.renderer.patch(this.vnode, newVNode, this.el, this.createRenderContext());
1004
- this.vnode = newVNode;
1005
- this.onUpdated();
1006
- } catch (error) {
1007
- console.error("组件更新错误:", error);
1267
+ this.beforeUpdate();
1268
+ const newVNode = this.render();
1269
+ const previousElement = this.el;
1270
+ this.el = this.renderer.patch(this.vnode, newVNode, this.el, this.createRenderContext());
1271
+ if (previousElement !== this.el) {
1272
+ this.elementChangeListener?.(previousElement, this.el);
1008
1273
  }
1274
+ this.vnode = newVNode;
1275
+ this.onUpdated();
1009
1276
  }
1010
1277
  unmount() {
1011
1278
  if (!this.mounted) {
@@ -1016,8 +1283,15 @@ class Component {
1016
1283
  this.renderer.unmount(this.vnode, this.el, this.createRenderContext());
1017
1284
  }
1018
1285
  this.childComponents.clear();
1286
+ Object.keys(this.eventListeners).forEach((eventName) => {
1287
+ this.eventListeners[eventName].clear();
1288
+ delete this.eventListeners[eventName];
1289
+ });
1290
+ this.providers.clear();
1291
+ this.parentComponent = null;
1292
+ this.elementChangeListener = null;
1019
1293
  this.templateEngine.clearBindings();
1020
- this.styleManager.clearStyles();
1294
+ this.styleManager.destroy();
1021
1295
  stop(this.updateEffect);
1022
1296
  if (this.el?.parentNode) {
1023
1297
  this.el.parentNode.removeChild(this.el);
@@ -1045,6 +1319,28 @@ class Component {
1045
1319
  child.setAppContext?.(context);
1046
1320
  });
1047
1321
  }
1322
+ setParentComponent(parent) {
1323
+ this.parentComponent = parent;
1324
+ }
1325
+ setElementChangeListener(listener) {
1326
+ this.elementChangeListener = listener;
1327
+ }
1328
+ provide(key, value) {
1329
+ this.providers.set(key, value);
1330
+ }
1331
+ inject(key, fallback) {
1332
+ const result = this.resolveInjection(key);
1333
+ return result.found ? result.value : fallback;
1334
+ }
1335
+ resolveInjection(key) {
1336
+ if (this.providers.has(key)) {
1337
+ return { found: true, value: this.providers.get(key) };
1338
+ }
1339
+ if (this.parentComponent?.resolveInjection) {
1340
+ return this.parentComponent.resolveInjection(key);
1341
+ }
1342
+ return this.resolveAppInjection(key);
1343
+ }
1048
1344
  getElement() {
1049
1345
  return this.el;
1050
1346
  }
@@ -1070,6 +1366,7 @@ class Component {
1070
1366
  this.eventListeners[eventName] = new Set;
1071
1367
  }
1072
1368
  this.eventListeners[eventName].add(listener);
1369
+ return () => this.off(eventName, listener);
1073
1370
  }
1074
1371
  off(eventName, listener) {
1075
1372
  this.eventListeners[eventName]?.delete(listener);
@@ -1082,7 +1379,12 @@ class Component {
1082
1379
  slots: this.collectSlots(),
1083
1380
  registerChild: (component) => {
1084
1381
  this.childComponents.add(component);
1382
+ component.setParentComponent?.(this);
1085
1383
  component.setAppContext?.(this.appContext);
1384
+ },
1385
+ unregisterChild: (component) => {
1386
+ this.childComponents.delete(component);
1387
+ component.setParentComponent?.(null);
1086
1388
  }
1087
1389
  };
1088
1390
  }
@@ -1125,6 +1427,13 @@ class Component {
1125
1427
  const globalApp = globalThis.__APP__;
1126
1428
  return this.getRouterFrom(globalApp);
1127
1429
  }
1430
+ resolveAppInjection(key) {
1431
+ if (!this.appContext || typeof this.appContext !== "object") {
1432
+ return { found: false, value: undefined };
1433
+ }
1434
+ const app = this.appContext.app;
1435
+ return app?.resolveInjection?.(key) ?? { found: false, value: undefined };
1436
+ }
1128
1437
  trackReactiveValue(value, seen) {
1129
1438
  if (!value || typeof value !== "object" || seen.has(value)) {
1130
1439
  return;
@@ -1147,6 +1456,12 @@ function setRouter(r) {
1147
1456
  }
1148
1457
  router = r;
1149
1458
  }
1459
+ function useRouter() {
1460
+ if (!router) {
1461
+ throw new Error("Router is not initialized. Please make sure you have installed the router plugin.");
1462
+ }
1463
+ return router;
1464
+ }
1150
1465
 
1151
1466
  // lib/router/matcher.ts
1152
1467
  function normalizePath(path) {
@@ -1207,13 +1522,18 @@ function createRouterHref(path, mode, base) {
1207
1522
  function getBrowserLocation(mode, base) {
1208
1523
  let path;
1209
1524
  let fullPath;
1525
+ let queryString;
1210
1526
  if (mode === "history") {
1211
1527
  fullPath = window.location.pathname + window.location.search;
1212
1528
  path = window.location.pathname;
1529
+ queryString = window.location.search;
1213
1530
  } else {
1214
1531
  const hash = window.location.hash;
1215
1532
  fullPath = hash || "#/";
1216
1533
  path = fullPath.startsWith("#") ? fullPath.slice(1) : fullPath;
1534
+ const queryStart = path.indexOf("?");
1535
+ queryString = queryStart >= 0 ? path.slice(queryStart + 1) : "";
1536
+ path = queryStart >= 0 ? path.slice(0, queryStart) : path;
1217
1537
  }
1218
1538
  if (path.startsWith(base) && base !== "/" && path !== "/") {
1219
1539
  path = path.slice(base.length);
@@ -1222,7 +1542,7 @@ function getBrowserLocation(mode, base) {
1222
1542
  return {
1223
1543
  path,
1224
1544
  fullPath,
1225
- query: parseQuery(mode === "hash" ? path.split("?")[1] ?? "" : window.location.search),
1545
+ query: parseQuery(queryString),
1226
1546
  params: {}
1227
1547
  };
1228
1548
  }
@@ -1492,7 +1812,7 @@ function createRouter(options) {
1492
1812
  return new Router(options);
1493
1813
  }
1494
1814
 
1495
- export { ReactiveSystem, reactive, readonly, effect, computed, stop, isReactive, isReadonly, TemplateEngine, isComponentNode, isHTMLNode, isSlotProvider, h, createComponent, slot, RendererContext, TextRenderStrategy, ComponentRenderStrategy, SlotRenderStrategy, ElementRenderStrategy, Component, Router, RouterLink, RouterView, createRouter };
1815
+ export { ReactiveSystem, reactive, readonly, effect, computed, ref, isRef, unref, stop, isReactive, isReadonly, TemplateEngine, modelPath, getModelValue, setModelValue, ModelBindingController, isComponentNode, isHTMLNode, isSlotProvider, h, Div, Span, P, Button, Input, createComponent, slot, each, RendererContext, TextRenderStrategy, ComponentRenderStrategy, SlotRenderStrategy, ElementRenderStrategy, Component, useRouter, Router, RouterLink, RouterView, createRouter };
1496
1816
 
1497
- //# debugId=97822D95A36A4F3E64756E2164756E21
1498
- //# sourceMappingURL=index-3j2jsdpc.js.map
1817
+ //# debugId=EA20D7B3780F196864756E2164756E21
1818
+ //# sourceMappingURL=index-ycmc7ga1.js.map