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