@estjs/template 0.0.15-beta.1 → 0.0.15-beta.5

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.
@@ -4,7 +4,7 @@ var shared = require('@estjs/shared');
4
4
  var signals = require('@estjs/signals');
5
5
 
6
6
  /**
7
- * @estjs/template v0.0.15-beta.1
7
+ * @estjs/template v0.0.15-beta.5
8
8
  * (c) 2023-Present jiangxd <jiangxd2016@gmail.com>
9
9
  * @license MIT
10
10
  **/
@@ -44,125 +44,117 @@ var __async = (__this, __arguments, generator) => {
44
44
  step((generator = generator.apply(__this, __arguments)).next());
45
45
  });
46
46
  };
47
- var activeContext = null;
48
- var contextStack = [];
49
- var contextId = 0;
50
- function createContext(parent = null) {
51
- const context = {
52
- id: ++contextId,
47
+ var activeScope = null;
48
+ var scopeId = 0;
49
+ var scopeStack = [];
50
+ function getActiveScope() {
51
+ return activeScope;
52
+ }
53
+ function setActiveScope(scope) {
54
+ activeScope = scope;
55
+ }
56
+ function createScope(parent = activeScope) {
57
+ const scope = {
58
+ id: ++scopeId,
53
59
  parent,
54
- provides: /* @__PURE__ */ new Map(),
55
- cleanup: /* @__PURE__ */ new Set(),
56
- mount: /* @__PURE__ */ new Set(),
57
- update: /* @__PURE__ */ new Set(),
58
- destroy: /* @__PURE__ */ new Set(),
59
- isMount: false,
60
- isDestroy: false,
61
- children: /* @__PURE__ */ new Set()
60
+ children: null,
61
+ // Lazy initialized
62
+ provides: null,
63
+ // Lazy initialized
64
+ cleanup: null,
65
+ // Lazy initialized
66
+ onMount: null,
67
+ // Lazy initialized
68
+ onUpdate: null,
69
+ // Lazy initialized
70
+ onDestroy: null,
71
+ // Lazy initialized
72
+ isMounted: false,
73
+ isDestroyed: false
62
74
  };
63
75
  if (parent) {
64
- parent.children.add(context);
65
- }
66
- return context;
67
- }
68
- function getActiveContext() {
69
- return activeContext;
70
- }
71
- function pushContextStack(context) {
72
- if (activeContext) {
73
- contextStack.push(activeContext);
74
- }
75
- activeContext = context;
76
- }
77
- function popContextStack() {
78
- activeContext = contextStack.pop() || null;
79
- }
80
- function destroyContext(context) {
81
- if (!context || context.isDestroy) {
82
- return;
76
+ if (!parent.children) {
77
+ parent.children = /* @__PURE__ */ new Set();
78
+ }
79
+ parent.children.add(scope);
83
80
  }
84
- const childrenToDestroy = Array.from(context.children);
85
- childrenToDestroy.forEach(destroyContext);
86
- cleanupContext(context);
81
+ return scope;
87
82
  }
88
- function cleanupContext(context) {
89
- if (!context || context.isDestroy) {
90
- return;
91
- }
92
- if (context.parent) {
93
- context.parent.children.delete(context);
94
- context.parent = null;
83
+ function runWithScope(scope, fn) {
84
+ const prevScope = activeScope;
85
+ if (prevScope) {
86
+ scopeStack.push(prevScope);
95
87
  }
88
+ activeScope = scope;
96
89
  try {
97
- context.cleanup.forEach((fn) => fn());
98
- context.cleanup.clear();
99
- context.mount.clear();
100
- context.update.clear();
101
- context.destroy.clear();
102
- context.provides.clear();
103
- context.children.clear();
104
- } catch (error_) {
105
- shared.error("Error during context cleanup:", error_);
90
+ return fn();
91
+ } finally {
92
+ activeScope = scopeStack.length > 0 ? scopeStack.pop() : prevScope;
106
93
  }
107
- context.isDestroy = true;
108
94
  }
109
- var LIFECYCLE = {
110
- mount: "mount",
111
- destroy: "destroy",
112
- update: "update"
113
- };
114
- function registerLifecycleHook(type, hook) {
115
- const context = getActiveContext();
116
- if (!context) {
117
- shared.error(`Cannot register ${type} hook outside component context`);
95
+ function disposeScope(scope) {
96
+ var _a2, _b, _c, _d, _e;
97
+ if (!scope || scope.isDestroyed) {
118
98
  return;
119
99
  }
120
- if (!LIFECYCLE[type]) {
121
- shared.error(`Invalid lifecycle type: ${type}`);
122
- return;
100
+ if (scope.children) {
101
+ const children = Array.from(scope.children);
102
+ for (const child of children) {
103
+ disposeScope(child);
104
+ }
123
105
  }
124
- if (type === LIFECYCLE.mount && context.isMount) {
125
- try {
126
- hook();
127
- } catch (error_) {
128
- shared.error(`Error in ${type} hook:`, error_);
106
+ if (scope.onDestroy) {
107
+ for (const hook of scope.onDestroy) {
108
+ try {
109
+ hook();
110
+ } catch (error_) {
111
+ {
112
+ shared.error(`Scope(${scope.id}): Error in destroy hook:`, error_);
113
+ }
114
+ }
129
115
  }
130
- return;
116
+ scope.onDestroy.clear();
131
117
  }
132
- context[type].add(hook);
118
+ if (scope.cleanup) {
119
+ for (const fn of scope.cleanup) {
120
+ try {
121
+ fn();
122
+ } catch (error_) {
123
+ {
124
+ shared.error(`Scope(${scope.id}): Error in cleanup:`, error_);
125
+ }
126
+ }
127
+ }
128
+ scope.cleanup.clear();
129
+ }
130
+ if ((_a2 = scope.parent) == null ? void 0 : _a2.children) {
131
+ scope.parent.children.delete(scope);
132
+ }
133
+ (_b = scope.children) == null ? void 0 : _b.clear();
134
+ (_c = scope.provides) == null ? void 0 : _c.clear();
135
+ (_d = scope.onMount) == null ? void 0 : _d.clear();
136
+ (_e = scope.onUpdate) == null ? void 0 : _e.clear();
137
+ setActiveScope(scope.parent);
138
+ scope.parent = null;
139
+ scope.isDestroyed = true;
133
140
  }
134
- function triggerLifecycleHook(type) {
135
- const context = getActiveContext();
136
- if (!context) {
137
- shared.error(`Cannot trigger ${type} hook outside component context`);
141
+ function onCleanup(fn) {
142
+ const scope = activeScope;
143
+ if (!scope) {
144
+ {
145
+ shared.error("onCleanup() must be called within a scope");
146
+ }
138
147
  return;
139
148
  }
140
- const hooks = context[type];
141
- if (!(hooks == null ? void 0 : hooks.size)) {
142
- return;
149
+ if (!scope.cleanup) {
150
+ scope.cleanup = /* @__PURE__ */ new Set();
143
151
  }
144
- hooks.forEach((hook) => {
145
- try {
146
- hook();
147
- } catch (error_) {
148
- {
149
- shared.error(`Error in ${type} lifecycle hook:`, error_);
150
- }
151
- }
152
- });
153
- }
154
- function onMount(hook) {
155
- registerLifecycleHook(LIFECYCLE.mount, hook);
156
- }
157
- function onDestroy(hook) {
158
- registerLifecycleHook(LIFECYCLE.destroy, hook);
159
- }
160
- function onUpdate(hook) {
161
- registerLifecycleHook(LIFECYCLE.update, hook);
152
+ scope.cleanup.add(fn);
162
153
  }
163
154
 
164
155
  // src/constants.ts
165
156
  var EVENT_PREFIX = "on";
157
+ var SPREAD_NAME = "_$spread$";
166
158
  var REF_KEY = "ref";
167
159
  var KEY_PROP = "key";
168
160
  var SVG_NAMESPACE = "http://www.w3.org/2000/svg";
@@ -252,6 +244,36 @@ function getNodeKey(node) {
252
244
  }
253
245
 
254
246
  // src/utils.ts
247
+ function omitProps(target, keys) {
248
+ const excludeSet = new Set(keys);
249
+ return new Proxy(target, {
250
+ // Intercept property reads
251
+ get(obj, prop) {
252
+ if (excludeSet.has(prop)) {
253
+ return void 0;
254
+ }
255
+ return Reflect.get(obj, prop);
256
+ },
257
+ // Intercept property enumeration (for...in, Object.keys, etc.)
258
+ ownKeys(obj) {
259
+ return Reflect.ownKeys(obj).filter((key) => !excludeSet.has(key));
260
+ },
261
+ // Intercept property descriptor retrieval
262
+ getOwnPropertyDescriptor(obj, prop) {
263
+ if (excludeSet.has(prop)) {
264
+ return void 0;
265
+ }
266
+ return Reflect.getOwnPropertyDescriptor(obj, prop);
267
+ },
268
+ // Intercept the 'in' operator
269
+ has(obj, prop) {
270
+ if (excludeSet.has(prop)) {
271
+ return false;
272
+ }
273
+ return Reflect.has(obj, prop);
274
+ }
275
+ });
276
+ }
255
277
  function removeNode(node) {
256
278
  if (!node) return;
257
279
  try {
@@ -267,7 +289,7 @@ function removeNode(node) {
267
289
  shared.error("Failed to remove node:", _error);
268
290
  }
269
291
  }
270
- function insertNode(parent, child, before = null) {
292
+ function insertNode(parent, child, before) {
271
293
  if (!parent || !child) return;
272
294
  try {
273
295
  const beforeNode = isComponent(before) ? before.firstChild : before;
@@ -292,19 +314,23 @@ function insertNode(parent, child, before = null) {
292
314
  function replaceNode(parent, newNode, oldNode) {
293
315
  if (!parent || !newNode || !oldNode || newNode === oldNode) return;
294
316
  try {
295
- insertNode(parent, newNode, oldNode);
317
+ const beforeNode = isComponent(oldNode) ? oldNode.beforeNode : oldNode.nextSibling;
296
318
  removeNode(oldNode);
319
+ insertNode(parent, newNode, beforeNode);
297
320
  } catch (_error) {
298
321
  shared.error("Failed to replace node:", _error);
299
322
  }
300
323
  }
301
324
  function getFirstDOMNode(node) {
302
325
  if (!node) {
303
- return null;
326
+ return;
304
327
  }
305
328
  if (isComponent(node)) {
306
329
  return node.firstChild;
307
330
  }
331
+ if (shared.isPrimitive(node)) {
332
+ return void 0;
333
+ }
308
334
  return node;
309
335
  }
310
336
  function isSameNode(a, b) {
@@ -321,6 +347,9 @@ function isSameNode(a, b) {
321
347
  if (aIsComponent !== bIsComponent) {
322
348
  return false;
323
349
  }
350
+ if (shared.isPrimitive(a) || shared.isPrimitive(b)) {
351
+ return a === b;
352
+ }
324
353
  const aNode = a;
325
354
  const bNode = b;
326
355
  if (aNode.nodeType !== bNode.nodeType) {
@@ -427,11 +456,9 @@ function patchChildren(parent, oldChildren, newChildren, anchor) {
427
456
  return [];
428
457
  }
429
458
  if (oldLength === 0) {
430
- const fragment = document.createDocumentFragment();
431
459
  for (let i = 0; i < newLength; i++) {
432
- insertNode(fragment, newChildren[i]);
460
+ insertNode(parent, newChildren[i], anchor);
433
461
  }
434
- insertNode(parent, fragment, anchor);
435
462
  return newChildren;
436
463
  }
437
464
  if (newLength === 0) {
@@ -663,12 +690,9 @@ function getSequence(arr) {
663
690
  // src/binding.ts
664
691
  function addEventListener(element, event, handler, options) {
665
692
  element.addEventListener(event, handler, options);
666
- const context = getActiveContext();
667
- if (context) {
668
- context.cleanup.add(() => {
669
- element.removeEventListener(event, handler, options);
670
- });
671
- }
693
+ onCleanup(() => {
694
+ element.removeEventListener(event, handler, options);
695
+ });
672
696
  }
673
697
  function bindElement(node, key, defaultValue, setter) {
674
698
  if (isHtmlInputElement(node)) {
@@ -724,23 +748,28 @@ function bindElement(node, key, defaultValue, setter) {
724
748
  });
725
749
  }
726
750
  }
727
- function insert(parent, nodeFactory, before, options) {
751
+ function insert(parent, nodeFactory, before) {
728
752
  if (!parent) return;
729
- const context = getActiveContext();
730
- if (!context) return;
753
+ const ownerScope = getActiveScope();
731
754
  let renderedNodes = [];
732
755
  const cleanup = signals.effect(() => {
733
- const rawNodes = shared.isFunction(nodeFactory) ? nodeFactory() : nodeFactory;
734
- const nodes = shared.coerceArray(rawNodes).map(normalizeNode);
735
- renderedNodes = patchChildren(parent, renderedNodes, nodes, before);
756
+ const executeUpdate = () => {
757
+ const rawNodes = shared.isFunction(nodeFactory) ? nodeFactory() : nodeFactory;
758
+ const nodes = shared.coerceArray(rawNodes).map((item) => shared.isFunction(item) ? item() : item).flatMap((i) => i).map(normalizeNode);
759
+ renderedNodes = patchChildren(parent, renderedNodes, nodes, before);
760
+ };
761
+ if (ownerScope && !ownerScope.isDestroyed) {
762
+ runWithScope(ownerScope, executeUpdate);
763
+ } else {
764
+ executeUpdate();
765
+ }
736
766
  });
737
- context.cleanup.add(() => {
767
+ onCleanup(() => {
738
768
  cleanup();
739
- if (!(options == null ? void 0 : options.preserveOnCleanup)) {
740
- renderedNodes.forEach((node) => removeNode(node));
741
- }
769
+ renderedNodes.forEach((node) => removeNode(node));
742
770
  renderedNodes.length = 0;
743
771
  });
772
+ return renderedNodes;
744
773
  }
745
774
  function mapNodes(template2, indexes) {
746
775
  const len = indexes.length;
@@ -766,6 +795,99 @@ function mapNodes(template2, indexes) {
766
795
  walk(template2);
767
796
  return tree;
768
797
  }
798
+ function registerMountHook(hook) {
799
+ const scope = getActiveScope();
800
+ if (!scope) {
801
+ {
802
+ shared.error("onMount() must be called within a scope");
803
+ }
804
+ return;
805
+ }
806
+ if (scope.isMounted) {
807
+ try {
808
+ hook();
809
+ } catch (error_) {
810
+ {
811
+ shared.error(`Scope(${scope.id}): Error in mount hook:`, error_);
812
+ }
813
+ }
814
+ return;
815
+ }
816
+ if (!scope.onMount) {
817
+ scope.onMount = /* @__PURE__ */ new Set();
818
+ }
819
+ scope.onMount.add(hook);
820
+ }
821
+ function registerUpdateHook(hook) {
822
+ const scope = getActiveScope();
823
+ if (!scope) {
824
+ {
825
+ shared.error("onUpdate() must be called within a scope");
826
+ }
827
+ return;
828
+ }
829
+ if (!scope.onUpdate) {
830
+ scope.onUpdate = /* @__PURE__ */ new Set();
831
+ }
832
+ scope.onUpdate.add(hook);
833
+ }
834
+ function registerDestroyHook(hook) {
835
+ const scope = getActiveScope();
836
+ if (!scope) {
837
+ {
838
+ shared.error("onDestroy() must be called within a scope");
839
+ }
840
+ return;
841
+ }
842
+ if (!scope.onDestroy) {
843
+ scope.onDestroy = /* @__PURE__ */ new Set();
844
+ }
845
+ scope.onDestroy.add(hook);
846
+ }
847
+ function triggerMountHooks(scope) {
848
+ if (!scope || scope.isDestroyed || scope.isMounted) {
849
+ return;
850
+ }
851
+ scope.isMounted = true;
852
+ if (scope.onMount) {
853
+ runWithScope(scope, () => {
854
+ for (const hook of scope.onMount) {
855
+ try {
856
+ hook();
857
+ } catch (error_) {
858
+ if (true) {
859
+ shared.error(`Scope(${scope.id}): Error in mount hook:`, error_);
860
+ }
861
+ }
862
+ }
863
+ });
864
+ }
865
+ }
866
+ function triggerUpdateHooks(scope) {
867
+ if (!scope || scope.isDestroyed) {
868
+ return;
869
+ }
870
+ if (scope.onUpdate) {
871
+ for (const hook of scope.onUpdate) {
872
+ try {
873
+ hook();
874
+ } catch (error_) {
875
+ {
876
+ shared.error(`Scope(${scope.id}): Error in update hook:`, error_);
877
+ }
878
+ }
879
+ }
880
+ }
881
+ }
882
+ function onMount(hook) {
883
+ registerMountHook(hook);
884
+ }
885
+ function onDestroy(hook) {
886
+ registerDestroyHook(hook);
887
+ }
888
+ function onUpdate(hook) {
889
+ registerUpdateHook(hook);
890
+ }
769
891
 
770
892
  // src/component.ts
771
893
  var _a;
@@ -774,71 +896,74 @@ var Component = class {
774
896
  constructor(component, props) {
775
897
  this.component = component;
776
898
  this.props = props;
777
- // component rendered node
778
- this.renderedNode = null;
779
- // component context
780
- this.componentContext = null;
899
+ // component rendered nodes (supports arrays and fragments)
900
+ this.renderedNodes = [];
901
+ // component scope (unified context management)
902
+ this.scope = null;
781
903
  // component parent node
782
- this.parentNode = null;
904
+ this.parentNode = void 0;
783
905
  // component before node
784
- this.beforeNode = null;
906
+ this.beforeNode = void 0;
785
907
  // component props
786
908
  this.reactiveProps = {};
787
- this._propSnapshots = {};
788
909
  // component state
789
910
  this.state = 0 /* INITIAL */;
790
- // component context
791
- this.context = null;
792
- // component parent context
793
- this.parentContext = null;
911
+ // parent scope captured at construction time
912
+ this.parentScope = null;
794
913
  // component type
795
914
  // @ts-ignore
796
915
  this[_a] = true;
797
916
  this.key = (props == null ? void 0 : props.key) ? normalizeKey(props.key) : getComponentKey(component);
798
917
  this.reactiveProps = signals.shallowReactive(__spreadValues({}, this.props || {}));
799
- this.parentContext = getActiveContext();
800
- if (this.props) {
801
- for (const key in this.props) {
802
- const val = this.props[key];
803
- if (shared.isObject(val) && val !== null) {
804
- this._propSnapshots[key] = Array.isArray(val) ? [...val] : __spreadValues({}, val);
805
- }
806
- }
807
- }
918
+ this.parentScope = getActiveScope();
808
919
  }
809
920
  get isConnected() {
810
921
  return this.state === 2 /* MOUNTED */;
811
922
  }
812
923
  get firstChild() {
813
- var _a2;
814
- return (_a2 = this.renderedNode) != null ? _a2 : null;
924
+ for (const node of this.renderedNodes) {
925
+ const dom = getFirstDOMNode(node);
926
+ if (dom) {
927
+ return dom;
928
+ }
929
+ }
930
+ return void 0;
815
931
  }
816
932
  mount(parentNode, beforeNode) {
933
+ var _a2;
817
934
  this.parentNode = parentNode;
818
- this.beforeNode = beforeNode || null;
935
+ this.beforeNode = beforeNode;
819
936
  this.state = 1 /* MOUNTING */;
820
- if (this.renderedNode) {
821
- insertNode(parentNode, this.renderedNode, beforeNode);
822
- return this.renderedNode;
823
- }
824
- this.componentContext = createContext(this.parentContext);
825
- pushContextStack(this.componentContext);
826
- let result = this.component(this.reactiveProps);
827
- if (shared.isFunction(result)) {
828
- result = result(this.reactiveProps);
829
- }
830
- if (signals.isSignal(result) || signals.isComputed(result)) {
831
- result = result.value;
832
- }
833
- this.renderedNode = result;
834
- insertNode(parentNode, this.renderedNode, beforeNode);
835
- this.applyProps(this.props || {});
937
+ if (this.renderedNodes.length > 0) {
938
+ for (const node of this.renderedNodes) {
939
+ insertNode(parentNode, node, beforeNode);
940
+ }
941
+ this.state = 2 /* MOUNTED */;
942
+ return this.renderedNodes;
943
+ }
944
+ const parent = (_a2 = this.parentScope) != null ? _a2 : getActiveScope();
945
+ this.scope = createScope(parent);
946
+ const renderedNodes = runWithScope(this.scope, () => {
947
+ var _a3;
948
+ let result = this.component(this.reactiveProps);
949
+ if (shared.isFunction(result)) {
950
+ result = result(this.reactiveProps);
951
+ }
952
+ if (signals.isSignal(result) || signals.isComputed(result)) {
953
+ result = result.value;
954
+ }
955
+ const nodes = (_a3 = insert(parentNode, result, beforeNode)) != null ? _a3 : [];
956
+ return nodes;
957
+ });
958
+ this.renderedNodes = renderedNodes;
959
+ runWithScope(this.scope, () => {
960
+ this.applyProps(this.props || {});
961
+ });
836
962
  this.state = 2 /* MOUNTED */;
837
- if (this.componentContext) {
838
- this.componentContext.isMount = true;
963
+ if (this.scope) {
964
+ triggerMountHooks(this.scope);
839
965
  }
840
- triggerLifecycleHook(LIFECYCLE.mount);
841
- return this.renderedNode;
966
+ return this.renderedNodes;
842
967
  }
843
968
  update(prevNode) {
844
969
  if (this.key !== prevNode.key) {
@@ -847,76 +972,79 @@ var Component = class {
847
972
  }
848
973
  this.parentNode = prevNode.parentNode;
849
974
  this.beforeNode = prevNode.beforeNode;
850
- this.componentContext = prevNode.componentContext;
851
- this.renderedNode = prevNode.renderedNode;
975
+ this.scope = prevNode.scope;
976
+ this.parentScope = prevNode.parentScope;
977
+ this.renderedNodes = prevNode.renderedNodes;
852
978
  this.state = prevNode.state;
853
979
  this.reactiveProps = prevNode.reactiveProps;
854
- this._propSnapshots = prevNode._propSnapshots;
855
980
  if (this.props) {
856
981
  for (const key in this.props) {
857
982
  if (key === "key") continue;
858
983
  const newValue = this.props[key];
859
984
  const oldValue = this.reactiveProps[key];
860
- if (shared.isObject(newValue) && newValue !== null) {
861
- const snapshot = this._propSnapshots[key];
862
- if (!snapshot || !shallowCompare(newValue, snapshot)) {
863
- const newSnapshot = Array.isArray(newValue) ? newValue.slice() : Object.assign({}, newValue);
864
- this.reactiveProps[key] = newSnapshot;
865
- this._propSnapshots[key] = newSnapshot;
866
- }
867
- } else {
868
- if (shared.hasChanged(newValue, oldValue)) {
869
- this.reactiveProps[key] = newValue;
870
- if (this._propSnapshots[key]) {
871
- delete this._propSnapshots[key];
872
- }
985
+ if (Object.is(oldValue, newValue)) {
986
+ continue;
987
+ }
988
+ if (shared.isObject(oldValue) && shared.isObject(newValue)) {
989
+ if (shallowCompare(oldValue, newValue)) {
990
+ continue;
873
991
  }
874
992
  }
993
+ this.reactiveProps[key] = newValue;
875
994
  }
876
995
  }
877
996
  if (!this.isConnected && this.parentNode) {
878
997
  this.mount(this.parentNode, this.beforeNode);
879
998
  }
880
- if (this.componentContext) {
881
- pushContextStack(this.componentContext);
882
- this.applyProps(this.props || {});
883
- triggerLifecycleHook(LIFECYCLE.update);
884
- popContextStack();
999
+ if (this.scope) {
1000
+ runWithScope(this.scope, () => {
1001
+ this.applyProps(this.props || {});
1002
+ });
1003
+ triggerUpdateHooks(this.scope);
885
1004
  }
886
1005
  return this;
887
1006
  }
888
1007
  forceUpdate() {
889
1008
  return __async(this, null, function* () {
890
- if (this.state === 5 /* DESTROYED */ || !this.parentNode || !this.componentContext) {
1009
+ yield Promise.resolve();
1010
+ if (this.state === 5 /* DESTROYED */ || !this.parentNode || !this.scope) {
891
1011
  return;
892
1012
  }
893
- const prevNode = this.renderedNode;
894
- let newNode;
1013
+ const originalNodes = [...this.renderedNodes];
895
1014
  try {
896
- if (this.componentContext) {
897
- pushContextStack(this.componentContext);
898
- }
899
- newNode = this.component(this.reactiveProps);
900
- if (shared.isFunction(newNode)) {
901
- newNode = newNode(this.reactiveProps);
902
- }
903
- if (signals.isSignal(newNode) || signals.isComputed(newNode)) {
904
- newNode = newNode.value;
905
- }
906
- if (prevNode && newNode && prevNode !== newNode) {
1015
+ runWithScope(this.scope, () => {
1016
+ let result = this.component(this.reactiveProps);
1017
+ if (shared.isFunction(result)) {
1018
+ result = result(this.reactiveProps);
1019
+ }
1020
+ if (signals.isSignal(result) || signals.isComputed(result)) {
1021
+ result = result.value;
1022
+ }
1023
+ const newNodes = shared.coerceArray(result);
907
1024
  if (this.parentNode) {
908
- replaceNode(this.parentNode, newNode, prevNode);
909
- this.renderedNode = newNode;
1025
+ let anchor = this.beforeNode;
1026
+ if (!anchor && this.renderedNodes.length > 0) {
1027
+ const lastNode = this.renderedNodes[this.renderedNodes.length - 1];
1028
+ const lastDom = getFirstDOMNode(lastNode);
1029
+ if (lastDom) {
1030
+ anchor = lastDom.nextSibling;
1031
+ }
1032
+ }
1033
+ for (const node of this.renderedNodes) {
1034
+ removeNode(node);
1035
+ }
1036
+ for (const node of newNodes) {
1037
+ insertNode(this.parentNode, node, anchor);
1038
+ }
1039
+ this.renderedNodes = newNodes;
910
1040
  }
1041
+ });
1042
+ if (this.scope) {
1043
+ triggerUpdateHooks(this.scope);
911
1044
  }
912
- yield triggerLifecycleHook(LIFECYCLE.update);
913
- } catch (_error) {
914
- shared.error("Force update failed:", _error);
915
- throw _error;
916
- } finally {
917
- if (this.componentContext) {
918
- popContextStack();
919
- }
1045
+ } catch (error10) {
1046
+ this.renderedNodes = originalNodes;
1047
+ throw error10;
920
1048
  }
921
1049
  });
922
1050
  }
@@ -928,21 +1056,18 @@ var Component = class {
928
1056
  return;
929
1057
  }
930
1058
  this.state = 4 /* DESTROYING */;
931
- const context = this.componentContext;
932
- if (context) {
933
- pushContextStack(context);
934
- triggerLifecycleHook(LIFECYCLE.destroy);
935
- destroyContext(context);
936
- this.componentContext = null;
937
- popContextStack();
938
- }
939
- const rendered = this.renderedNode;
940
- if (rendered) {
941
- removeNode(rendered);
942
- }
943
- this.renderedNode = null;
944
- this.parentNode = null;
945
- this.beforeNode = null;
1059
+ const scope = this.scope;
1060
+ if (scope) {
1061
+ disposeScope(scope);
1062
+ this.scope = null;
1063
+ }
1064
+ for (const node of this.renderedNodes) {
1065
+ removeNode(node);
1066
+ }
1067
+ this.renderedNodes = [];
1068
+ this.parentNode = void 0;
1069
+ this.beforeNode = void 0;
1070
+ this.parentScope = null;
946
1071
  this.reactiveProps = {};
947
1072
  this.props = void 0;
948
1073
  this.state = 5 /* DESTROYED */;
@@ -951,14 +1076,15 @@ var Component = class {
951
1076
  if (!props) {
952
1077
  return;
953
1078
  }
1079
+ const firstElement = this.firstChild;
954
1080
  for (const [propName, propValue] of Object.entries(props)) {
955
- if (shared.startsWith(propName, EVENT_PREFIX) && this.renderedNode) {
1081
+ if (shared.startsWith(propName, EVENT_PREFIX) && firstElement) {
956
1082
  const eventName = propName.slice(2).toLowerCase();
957
- if (shared.isHTMLElement(this.renderedNode)) {
958
- addEventListener(this.renderedNode, eventName, propValue);
1083
+ if (shared.isHTMLElement(firstElement)) {
1084
+ addEventListener(firstElement, eventName, propValue);
959
1085
  }
960
1086
  } else if (propName === REF_KEY && signals.isSignal(propValue)) {
961
- propValue.value = this.renderedNode;
1087
+ propValue.value = firstElement;
962
1088
  }
963
1089
  }
964
1090
  this.props = props;
@@ -1004,26 +1130,37 @@ function createApp(component, target) {
1004
1130
  return rootComponent;
1005
1131
  }
1006
1132
  function provide(key, value) {
1007
- const context = getActiveContext();
1008
- if (!context) {
1009
- shared.error("provide must be called within a template");
1133
+ const scope = getActiveScope();
1134
+ if (!scope) {
1135
+ {
1136
+ shared.error("provide() must be called within a scope");
1137
+ }
1010
1138
  return;
1011
1139
  }
1012
- context.provides.set(key, value);
1140
+ if (!scope.provides) {
1141
+ scope.provides = /* @__PURE__ */ new Map();
1142
+ }
1143
+ scope.provides.set(key, value);
1013
1144
  }
1014
1145
  function inject(key, defaultValue) {
1015
- const context = getActiveContext();
1016
- if (!context) {
1017
- shared.error("inject must be called within a template");
1146
+ const scope = getActiveScope();
1147
+ console.log(scope);
1148
+ if (!scope) {
1149
+ {
1150
+ shared.error("inject() must be called within a scope");
1151
+ }
1018
1152
  return defaultValue;
1019
1153
  }
1020
- let currentContext = context;
1021
- while (currentContext) {
1022
- const value = currentContext.provides.get(key);
1023
- if (!shared.isNil(value)) {
1024
- return value;
1154
+ let current = scope;
1155
+ while (current) {
1156
+ if (current.provides) {
1157
+ console.log(current.provides);
1158
+ const value = current.provides.get(key);
1159
+ if (value) {
1160
+ return value;
1161
+ }
1025
1162
  }
1026
- currentContext = currentContext.parent;
1163
+ current = current.parent;
1027
1164
  }
1028
1165
  return defaultValue;
1029
1166
  }
@@ -1209,10 +1346,6 @@ function autoPrefix(style, rawName) {
1209
1346
  return rawName;
1210
1347
  }
1211
1348
  function patchAttr(el, key, prev, next) {
1212
- if (key === REF_KEY) {
1213
- prev.value = el;
1214
- return;
1215
- }
1216
1349
  if (key === KEY_PROP) {
1217
1350
  if (next == null) {
1218
1351
  setNodeKey(el, void 0);
@@ -1221,6 +1354,17 @@ function patchAttr(el, key, prev, next) {
1221
1354
  }
1222
1355
  return;
1223
1356
  }
1357
+ if (key === SPREAD_NAME) {
1358
+ {
1359
+ if (!shared.isObject(next)) {
1360
+ shared.warn("spread attribute must be an object");
1361
+ }
1362
+ }
1363
+ Object.keys(next).forEach((k) => {
1364
+ patchAttr(el, k, prev == null ? void 0 : prev[k], next == null ? void 0 : next[k]);
1365
+ });
1366
+ return;
1367
+ }
1224
1368
  const elementIsSVG = (el == null ? void 0 : el.namespaceURI) === SVG_NAMESPACE;
1225
1369
  const isXlink = elementIsSVG && key.startsWith("xlink:");
1226
1370
  const isXmlns = elementIsSVG && key.startsWith("xmlns:");
@@ -1305,29 +1449,18 @@ function addEvent(el, event, handler, options) {
1305
1449
  el.removeEventListener(event, wrappedHandler, cleanOptions);
1306
1450
  };
1307
1451
  }
1308
-
1309
- // src/components/Fragment.ts
1310
1452
  function Fragment(props) {
1311
- if (typeof document === "undefined") {
1312
- const children2 = props.children;
1313
- if (!children2) return "";
1314
- const childArray = Array.isArray(children2) ? children2 : [children2];
1315
- return childArray.map((child) => String(child || "")).join("");
1316
- }
1317
- const fragment = document.createDocumentFragment();
1318
- const children = props.children;
1319
- if (children) {
1320
- const childArray = Array.isArray(children) ? children : [children];
1321
- childArray.forEach((child) => {
1322
- if (child != null) {
1323
- const normalized = normalizeNode(child);
1324
- if (normalized) {
1325
- insertNode(fragment, normalized);
1326
- }
1327
- }
1328
- });
1453
+ {
1454
+ if (!props) {
1455
+ shared.error("Fragment component requires props");
1456
+ return null;
1457
+ }
1458
+ if (!props.children) {
1459
+ shared.error("Fragment component requires children");
1460
+ return null;
1461
+ }
1329
1462
  }
1330
- return fragment;
1463
+ return props == null ? void 0 : props.children;
1331
1464
  }
1332
1465
  Fragment["fragment" /* FRAGMENT */] = true;
1333
1466
  function isFragment(node) {
@@ -1335,34 +1468,43 @@ function isFragment(node) {
1335
1468
  }
1336
1469
  function Portal(props) {
1337
1470
  if (typeof document === "undefined") {
1338
- const children = props.children;
1339
- if (!children) return "";
1340
- const childArray = shared.isArray(children) ? children : [children];
1471
+ const children2 = props.children;
1472
+ if (!children2) return "";
1473
+ const childArray = shared.isArray(children2) ? children2 : [children2];
1341
1474
  return childArray.map((child) => String(child || "")).join("");
1342
1475
  }
1343
1476
  const placeholder = document.createComment("portal");
1344
1477
  placeholder["portal" /* PORTAL */] = true;
1345
- onMount(() => {
1346
- const targetElement = shared.isString(props.target) ? document.querySelector(props.target) : props.target;
1347
- if (!targetElement) {
1348
- {
1349
- shared.warn(`[Portal] Target element not found: ${props.target}`);
1478
+ const children = props.children;
1479
+ if (children) {
1480
+ const childArray = shared.isArray(children) ? children : [children];
1481
+ const nodes = [];
1482
+ onMount(() => {
1483
+ const targetElement = shared.isString(props.target) ? document.querySelector(props.target) : props.target;
1484
+ if (!targetElement) {
1485
+ {
1486
+ shared.warn(`[Portal] Target element not found: ${props.target}`);
1487
+ }
1488
+ return;
1350
1489
  }
1351
- return;
1352
- }
1353
- const children = props.children;
1354
- if (children) {
1355
- const childArray = shared.isArray(children) ? children : [children];
1356
1490
  childArray.forEach((child) => {
1357
1491
  if (child != null) {
1358
1492
  const normalized = normalizeNode(child);
1359
1493
  if (normalized) {
1360
1494
  insertNode(targetElement, normalized);
1495
+ nodes.push(normalized);
1361
1496
  }
1362
1497
  }
1363
1498
  });
1364
- }
1365
- });
1499
+ onCleanup(() => {
1500
+ nodes.forEach((node) => {
1501
+ if (typeof node !== "string" && node.parentNode === targetElement) {
1502
+ targetElement.removeChild(node);
1503
+ }
1504
+ });
1505
+ });
1506
+ });
1507
+ }
1366
1508
  return placeholder;
1367
1509
  }
1368
1510
  Portal["portal" /* PORTAL */] = true;
@@ -1418,12 +1560,12 @@ function Suspense(props) {
1418
1560
  container.removeChild(container.firstChild);
1419
1561
  }
1420
1562
  if (children2 == null) return;
1421
- const currentContext = getActiveContext();
1563
+ const currentScope = getActiveScope();
1422
1564
  const childArray = shared.isArray(children2) ? children2 : [children2];
1423
1565
  childArray.forEach((child) => {
1424
1566
  if (child != null) {
1425
1567
  if (isComponent(child)) {
1426
- child.parentContext = currentContext;
1568
+ child.parentContext = currentScope;
1427
1569
  }
1428
1570
  const normalized = normalizeNode(child);
1429
1571
  if (normalized) {
@@ -1824,6 +1966,7 @@ exports.isSuspense = isSuspense;
1824
1966
  exports.mapNodes = mapNodes;
1825
1967
  exports.mapSSRNodes = mapSSRNodes;
1826
1968
  exports.normalizeClass = normalizeClass;
1969
+ exports.omitProps = omitProps;
1827
1970
  exports.onDestroy = onDestroy;
1828
1971
  exports.onMount = onMount;
1829
1972
  exports.onUpdate = onUpdate;