@estjs/template 0.0.14-beta.2 → 0.0.14-beta.21

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,45 +4,40 @@ var shared = require('@estjs/shared');
4
4
  var signal = require('@estjs/signal');
5
5
 
6
6
  /**
7
- * @estjs/template v0.0.14-beta.2
7
+ * @estjs/template v0.0.14-beta.21
8
8
  * (c) 2023-Present jiangxd <jiangxd2016@gmail.com>
9
9
  * @license MIT
10
10
  **/
11
11
 
12
12
 
13
- // src/shared-config.ts
13
+ // src/sharedConfig.ts
14
14
  var EVENT_PREFIX = "on";
15
15
  var UPDATE_PREFIX = "update";
16
16
  var CHILDREN_PROP = "children";
17
17
  var EMPTY_TEMPLATE = "";
18
18
  var FRAGMENT_PROP_KEY = "0";
19
19
  var SINGLE_PROP_KEY = "1";
20
+ var REF_KEY = "ref";
20
21
  var PLACEHOLDER = " __PLACEHOLDER__ ";
21
22
  var RenderContext = class {
22
23
  constructor() {
23
24
  this.renderMode = 0 /* CLIENT */;
24
25
  }
25
- // Getter to check if the current mode is SSG
26
26
  get isSSG() {
27
27
  return this.renderMode === 1 /* SSG */;
28
28
  }
29
- // Getter to check if the current mode is SSR
30
29
  get isSSR() {
31
30
  return this.renderMode === 2 /* SSR */;
32
31
  }
33
- // Getter to check if the current mode is Client
34
32
  get isClient() {
35
33
  return this.renderMode === 0 /* CLIENT */;
36
34
  }
37
- // Set render mode to SSR
38
35
  setSSR() {
39
36
  this.renderMode = 2 /* SSR */;
40
37
  }
41
- // Set render mode to SSG
42
38
  setSSG() {
43
39
  this.renderMode = 1 /* SSG */;
44
40
  }
45
- // Set render mode to Client
46
41
  setClient() {
47
42
  this.renderMode = 0 /* CLIENT */;
48
43
  }
@@ -59,7 +54,7 @@ function getComponentIndex(temp) {
59
54
  return (_a = componentMap.get(temp)) == null ? void 0 : _a.index;
60
55
  }
61
56
 
62
- // src/lifecycle-context.ts
57
+ // src/lifecycleContext.ts
63
58
  var _LifecycleContext = class _LifecycleContext {
64
59
  constructor() {
65
60
  // Hooks for different lifecycle stages
@@ -85,11 +80,9 @@ var _LifecycleContext = class _LifecycleContext {
85
80
  setContext(context, value) {
86
81
  _LifecycleContext.context[context] = value;
87
82
  }
88
- // Initialize the static reference
89
83
  initRef() {
90
84
  _LifecycleContext.ref = this;
91
85
  }
92
- // Remove the static reference
93
86
  removeRef() {
94
87
  _LifecycleContext.ref = null;
95
88
  }
@@ -98,13 +91,11 @@ var _LifecycleContext = class _LifecycleContext {
98
91
  Object.values(this.hooks).forEach((set) => set.clear());
99
92
  }
100
93
  };
101
- // Static reference to the current context
102
94
  _LifecycleContext.ref = null;
103
- // Static context to store shared values
104
95
  _LifecycleContext.context = {};
105
96
  var LifecycleContext = _LifecycleContext;
106
97
 
107
- // src/ssg-node.ts
98
+ // src/ssgNode.ts
108
99
  function isSSGNode(node) {
109
100
  return node instanceof SSGNode;
110
101
  }
@@ -118,7 +109,6 @@ var SSGNode = class extends LifecycleContext {
118
109
  enterComponent(template, componentIndex);
119
110
  this.templates = this.processTemplate();
120
111
  }
121
- // Process the template and return an array of processed strings
122
112
  processTemplate() {
123
113
  if (shared.isArray(this.template)) {
124
114
  const htmlString = this.template.join(PLACEHOLDER);
@@ -127,7 +117,6 @@ var SSGNode = class extends LifecycleContext {
127
117
  }
128
118
  return [];
129
119
  }
130
- // Process HTML string by adding component index and handling text nodes
131
120
  processHtmlString(htmlString) {
132
121
  return htmlString.replaceAll(/(<[^>]+>)|([^<]+)/g, (match, p1, p2) => {
133
122
  if (p1) {
@@ -141,14 +130,12 @@ var SSGNode = class extends LifecycleContext {
141
130
  return match;
142
131
  });
143
132
  }
144
- // Mount the SSGNode and return the rendered string
145
133
  mount() {
146
134
  this.initRef();
147
135
  const output = this.render();
148
136
  this.removeRef();
149
137
  return output;
150
138
  }
151
- // Render the SSGNode
152
139
  render() {
153
140
  if (shared.isFunction(this.template)) {
154
141
  const root = this.template(this.props);
@@ -160,7 +147,6 @@ var SSGNode = class extends LifecycleContext {
160
147
  }
161
148
  return this.renderTemplate();
162
149
  }
163
- // Render the template by processing props and children
164
150
  renderTemplate() {
165
151
  Object.entries(this.props).forEach(([key, cur]) => {
166
152
  const children = cur.children;
@@ -176,10 +162,9 @@ var SSGNode = class extends LifecycleContext {
176
162
  });
177
163
  return this.templates.join("");
178
164
  }
179
- // Normalize props by removing children and handling signals
180
165
  normalizeProps(props) {
181
166
  Object.entries(props).forEach(([key, value]) => {
182
- if (key === "children") {
167
+ if (key === CHILDREN_PROP) {
183
168
  delete props[key];
184
169
  } else if (shared.isFunction(value)) {
185
170
  delete props[key];
@@ -188,11 +173,9 @@ var SSGNode = class extends LifecycleContext {
188
173
  }
189
174
  });
190
175
  }
191
- // Generate HTML attributes string from props
192
176
  generateAttributes(props) {
193
- return Object.entries(props).filter(([key, value]) => key !== "children" && !shared.isFunction(value)).map(([key, value]) => `${key}="${shared.escape(String(value))}"`).join(" ");
177
+ return Object.entries(props).filter(([key, value]) => key !== CHILDREN_PROP && !shared.isFunction(value)).map(([key, value]) => `${key}="${shared.escape(String(value))}"`).join(" ");
194
178
  }
195
- // Render children and append them to the template
196
179
  renderChildren(children, findIndex) {
197
180
  children.forEach(([child]) => {
198
181
  componentIndex++;
@@ -200,12 +183,11 @@ var SSGNode = class extends LifecycleContext {
200
183
  this.templates[findIndex] += renderedChild;
201
184
  });
202
185
  }
203
- // Render a single child node
204
186
  renderChild(child) {
205
- if (shared.isFunction(child)) {
206
- return this.renderChild(child(this.props));
207
- } else if (signal.isSignal(child)) {
187
+ if (signal.isSignal(child)) {
208
188
  return `<!--${1 /* TEXT_COMPONENT */}-${componentIndex}-->${child.value}<!$>`;
189
+ } else if (shared.isFunction(child)) {
190
+ return this.renderChild(child(this.props));
209
191
  } else if (isSSGNode(child)) {
210
192
  const childResult = child.mount();
211
193
  return shared.isFunction(childResult) ? childResult(this.props) : extractSignal(childResult);
@@ -217,9 +199,6 @@ var SSGNode = class extends LifecycleContext {
217
199
 
218
200
  // src/utils.ts
219
201
  var SELF_CLOSING_TAGS = "area,base,br,col,embed,hr,img,input,link,meta,param,source,track,wbr".split(",");
220
- var HTML_TAGS = "a,abbr,acronym,address,applet,area,article,aside,audio,b,base,basefont,bdi,bdo,bgsound,big,blink,blockquote,body,br,button,canvas,caption,center,cite,code,col,colgroup,command,content,data,datalist,dd,del,details,dfn,dialog,dir,div,dl,dt,em,embed,fieldset,figcaption,figure,font,footer,form,frame,frameset,h1,h2,h3,h4,h5,h6,head,header,hgroup,hr,html,i,iframe,image,img,input,ins,kbd,keygen,label,legend,li,link,listing,main,map,mark,marquee,menu,menuitem,meta,meter,nav,nobr,noframes,noscript,object,ol,optgroup,option,output,p,param,picture,plaintext,pre,progress,q,rb,rp,rt,rtc,ruby,s,samp,script,section,select,shadow,small,source,spacer,span,strike,strong,style,sub,summary,sup,table,tbody,td,template,textarea,tfoot,th,thead,time,title,tr,track,tt,u,ul,var,video,wbr,xmp".split(
221
- ","
222
- );
223
202
  function coerceNode(data) {
224
203
  if (isJsxElement(data) || data instanceof Node || isSSGNode(data)) {
225
204
  return data;
@@ -336,50 +315,10 @@ function bindNode(node, setter) {
336
315
  });
337
316
  }
338
317
  }
339
- Promise.resolve();
340
318
  function addEventListener(node, eventName, handler) {
341
319
  node.addEventListener(eventName, handler);
342
320
  return () => node.removeEventListener(eventName, handler);
343
321
  }
344
- function closeHtmlTags(input) {
345
- const tagStack = [];
346
- const output = [];
347
- const tagPattern = /<\/?([\da-z-]+)([^>]*)>/gi;
348
- let lastIndex = 0;
349
- while (true) {
350
- const match = tagPattern.exec(input);
351
- if (!match) break;
352
- const [fullMatch, tagName] = match;
353
- const isEndTag = fullMatch[1] === "/";
354
- output.push(input.slice(lastIndex, match.index));
355
- lastIndex = match.index + fullMatch.length;
356
- if (isEndTag) {
357
- while (tagStack.length > 0 && tagStack[tagStack.length - 1] !== tagName) {
358
- const unclosedTag = tagStack.pop();
359
- if (unclosedTag) {
360
- output.push(`</${unclosedTag}>`);
361
- }
362
- }
363
- if (tagStack.length > 0) {
364
- tagStack.pop();
365
- }
366
- } else if (!SELF_CLOSING_TAGS.includes(tagName)) {
367
- tagStack.push(tagName);
368
- }
369
- output.push(fullMatch);
370
- }
371
- output.push(input.slice(lastIndex));
372
- while (tagStack.length > 0) {
373
- const unclosedTag = tagStack.pop();
374
- if (unclosedTag) {
375
- output.push(`</${unclosedTag}>`);
376
- }
377
- }
378
- return output.join("");
379
- }
380
- function isHtmlTagName(tagName) {
381
- return HTML_TAGS.includes(tagName);
382
- }
383
322
  function convertToHtmlTag(tagName) {
384
323
  return SELF_CLOSING_TAGS.includes(tagName) ? `<${tagName}/>` : `<${tagName}></${tagName}>`;
385
324
  }
@@ -399,30 +338,30 @@ var ComponentNode = class extends LifecycleContext {
399
338
  this.emitter = /* @__PURE__ */ new Set();
400
339
  this.rootNode = null;
401
340
  this.trackMap = /* @__PURE__ */ new Map();
402
- this.key || (this.key = props == null ? void 0 : props.key);
403
- this.proxyProps = this.createProxyProps(props);
341
+ this.nodes = [];
342
+ this.parent = null;
343
+ this.before = null;
344
+ this.key || (this.key = props && props.key);
345
+ this.proxyProps || (this.proxyProps = this.createProxyProps(props));
404
346
  }
405
- // Create reactive props
406
347
  createProxyProps(props) {
407
348
  if (!props) return {};
408
- return signal.useReactive(
349
+ return signal.shallowReactive(
409
350
  props,
410
351
  (key) => shared.startsWith(key, EVENT_PREFIX) || shared.startsWith(key, UPDATE_PREFIX) || key === CHILDREN_PROP
411
352
  );
412
353
  }
413
- // Getter for the first child node
414
354
  get firstChild() {
415
355
  var _a, _b;
416
356
  return (_b = (_a = this.rootNode) == null ? void 0 : _a.firstChild) != null ? _b : null;
417
357
  }
418
- // Getter to check if the node is connected to the DOM
419
358
  get isConnected() {
420
359
  var _a, _b;
421
360
  return (_b = (_a = this.rootNode) == null ? void 0 : _a.isConnected) != null ? _b : false;
422
361
  }
423
- // Method to mount the component to the DOM
424
362
  mount(parent, before) {
425
363
  var _a, _b, _c, _d;
364
+ this.parent = parent;
426
365
  if (!shared.isFunction(this.template)) {
427
366
  throw new Error("Template must be a function");
428
367
  }
@@ -430,49 +369,55 @@ var ComponentNode = class extends LifecycleContext {
430
369
  return (_b = (_a = this.rootNode) == null ? void 0 : _a.mount(parent, before)) != null ? _b : [];
431
370
  }
432
371
  this.initRef();
433
- this.rootNode = this.template(this.proxyProps);
434
- const mountedNode = (_d = (_c = this.rootNode) == null ? void 0 : _c.mount(parent, before)) != null ? _d : [];
372
+ this.rootNode = this.template(signal.reactive(this.proxyProps, [CHILDREN_PROP]));
373
+ this.nodes = (_d = (_c = this.rootNode) == null ? void 0 : _c.mount(parent, before)) != null ? _d : [];
435
374
  this.callMountHooks();
436
375
  this.patchProps(this.props);
437
376
  this.removeRef();
438
- return mountedNode;
377
+ return this.nodes;
439
378
  }
440
- // Method to unmount the component from the DOM
441
379
  unmount() {
442
380
  var _a;
443
- this.callDestroyHooks();
444
- this.clearHooks();
381
+ this.callLifecycleHooks("destroy");
382
+ this.cleanup();
445
383
  (_a = this.rootNode) == null ? void 0 : _a.unmount();
384
+ this.resetState();
385
+ if (this.key) {
386
+ componentCache.delete(this.key);
387
+ }
388
+ }
389
+ resetState() {
446
390
  this.rootNode = null;
447
391
  this.proxyProps = {};
448
- this.clearEmitter();
392
+ this.nodes = [];
393
+ this.parent = null;
394
+ }
395
+ callLifecycleHooks(type) {
396
+ this.hooks[type].forEach((handler) => handler());
449
397
  }
450
- // Private method to call mount hooks
451
398
  callMountHooks() {
452
399
  this.hooks.mounted.forEach((handler) => handler());
453
400
  }
454
- // Private method to call destroy hooks
455
401
  callDestroyHooks() {
456
402
  this.hooks.destroy.forEach((handler) => handler());
457
403
  }
458
- // Private method to clear the event emitter
459
404
  clearEmitter() {
460
405
  for (const cleanup of this.emitter) {
461
406
  cleanup();
462
407
  }
463
408
  this.emitter.clear();
464
409
  }
465
- // Method to inherit properties from another ComponentNode
466
410
  inheritNode(node) {
467
411
  Object.assign(this.proxyProps, node.proxyProps);
468
412
  this.rootNode = node.rootNode;
469
413
  this.trackMap = node.trackMap;
470
414
  this.hooks = node.hooks;
471
- const props = this.props;
472
- this.props = node.props;
473
- this.patchProps(props);
415
+ if (shared.hasChanged(node.props, this.props)) {
416
+ const props = this.props;
417
+ this.props = node.props;
418
+ this.patchProps(props);
419
+ }
474
420
  }
475
- // Private method to get or create a NodeTrack
476
421
  getNodeTrack(trackKey) {
477
422
  let track = this.trackMap.get(trackKey);
478
423
  if (!track) {
@@ -483,7 +428,6 @@ var ComponentNode = class extends LifecycleContext {
483
428
  track.cleanup();
484
429
  return track;
485
430
  }
486
- // Method to patch props onto the component
487
431
  patchProps(props) {
488
432
  var _a;
489
433
  if (!props) {
@@ -492,7 +436,7 @@ var ComponentNode = class extends LifecycleContext {
492
436
  for (const [key, prop] of Object.entries(props)) {
493
437
  if (shared.startsWith(key, EVENT_PREFIX) && ((_a = this.rootNode) == null ? void 0 : _a.firstChild)) {
494
438
  this.patchEventListener(key, prop);
495
- } else if (key === "ref") {
439
+ } else if (key === REF_KEY) {
496
440
  this.patchRef(prop);
497
441
  } else if (shared.startsWith(key, UPDATE_PREFIX)) {
498
442
  this.patchUpdateHandler(key, prop);
@@ -502,28 +446,33 @@ var ComponentNode = class extends LifecycleContext {
502
446
  }
503
447
  this.props = props;
504
448
  }
505
- // Private method to patch event listeners
506
449
  patchEventListener(key, prop) {
507
450
  const event = key.slice(2).toLowerCase();
508
451
  const cleanup = addEventListener(this.rootNode.nodes[0], event, prop);
509
452
  this.emitter.add(cleanup);
510
453
  }
511
- // Private method to patch ref
512
454
  patchRef(prop) {
513
455
  var _a, _b;
514
456
  prop.value = (_b = (_a = this.rootNode) == null ? void 0 : _a.firstChild) != null ? _b : null;
515
457
  }
516
- // Private method to patch update handlers
517
458
  patchUpdateHandler(key, prop) {
518
- this.props[key] = extractSignal(prop);
459
+ this.proxyProps[key] = extractSignal(prop);
519
460
  }
520
- // Private method to patch normal props
521
461
  patchNormalProp(key, prop) {
522
462
  const track = this.getNodeTrack(key);
523
- track.cleanup = signal.useEffect(() => {
463
+ track.cleanup = signal.effect(() => {
524
464
  this.proxyProps[key] = shared.isFunction(prop) ? prop() : prop;
525
465
  });
526
466
  }
467
+ cleanup() {
468
+ this.trackMap.forEach((track) => {
469
+ var _a;
470
+ return (_a = track.cleanup) == null ? void 0 : _a.call(track);
471
+ });
472
+ this.trackMap.clear();
473
+ this.emitter.forEach((cleanup) => cleanup());
474
+ this.emitter.clear();
475
+ }
527
476
  };
528
477
 
529
478
  // src/patch.ts
@@ -633,39 +582,33 @@ function getKey(node, index) {
633
582
  return `_$${index}$`;
634
583
  }
635
584
 
636
- // src/template-node.ts
585
+ // src/templateNode.ts
637
586
  var TemplateNode = class {
638
587
  constructor(template, props, key) {
639
588
  this.template = template;
640
589
  this.props = props;
641
590
  this.key = key;
642
- // Private properties for managing the node's state
643
591
  this.treeMap = /* @__PURE__ */ new Map();
644
592
  this.mounted = false;
645
593
  this.nodes = [];
646
594
  this.trackMap = /* @__PURE__ */ new Map();
647
595
  this.bindValueKeys = [];
648
596
  this.parent = null;
649
- this.key || (this.key = props == null ? void 0 : props.key);
650
597
  if (renderContext.isSSR) {
651
598
  this.componentIndex = getComponentIndex(this.template);
652
599
  }
653
600
  }
654
- // Getter for the first child node
655
601
  get firstChild() {
656
602
  var _a;
657
603
  return (_a = this.nodes[0]) != null ? _a : null;
658
604
  }
659
- // Getter to check if the node is connected to the DOM
660
605
  get isConnected() {
661
606
  return this.mounted;
662
607
  }
663
- // Placeholder methods for event handling
664
608
  addEventListener() {
665
609
  }
666
610
  removeEventListener() {
667
611
  }
668
- // Method to mount the node to the DOM
669
612
  mount(parent, before) {
670
613
  var _a;
671
614
  this.parent = parent;
@@ -680,7 +623,7 @@ var TemplateNode = class {
680
623
  const firstChild = cloneNode.firstChild;
681
624
  if ((_a = firstChild == null ? void 0 : firstChild.hasAttribute) == null ? void 0 : _a.call(firstChild, "_svg_")) {
682
625
  firstChild.remove();
683
- firstChild == null ? void 0 : firstChild.childNodes.forEach((node) => {
626
+ firstChild.childNodes.forEach((node) => {
684
627
  cloneNode.append(node);
685
628
  });
686
629
  }
@@ -695,45 +638,19 @@ var TemplateNode = class {
695
638
  this.mounted = true;
696
639
  return this.nodes;
697
640
  }
698
- // Method to unmount the node from the DOM
699
641
  unmount() {
700
- var _a, _b;
701
642
  this.trackMap.forEach((track) => {
702
- var _a2;
703
- (_a2 = track.cleanup) == null ? void 0 : _a2.call(track);
643
+ track.cleanup && track.cleanup();
704
644
  });
705
645
  this.trackMap.clear();
706
646
  this.treeMap.clear();
707
647
  this.nodes.forEach((node) => removeChild(node));
708
- if (!this.template.innerHTML && !this.nodes.length) {
709
- const children = (_b = (_a = this.props) == null ? void 0 : _a[FRAGMENT_PROP_KEY]) == null ? void 0 : _b.children;
710
- if (children) {
711
- if (shared.isArray(children)) {
712
- children.forEach((child) => {
713
- this.deleteFragmentTextNode(child);
714
- });
715
- } else {
716
- this.deleteFragmentTextNode(children);
717
- }
718
- }
719
- }
720
648
  this.nodes = [];
721
649
  this.mounted = false;
722
- }
723
- deleteFragmentTextNode(child) {
724
- var _a;
725
- if (shared.isPrimitive(child)) {
726
- (_a = this.parent) == null ? void 0 : _a.childNodes.forEach((node) => {
727
- var _a2;
728
- if (node.nodeType === Node.TEXT_NODE && node.textContent === `${child}`) {
729
- (_a2 = this.parent) == null ? void 0 : _a2.removeChild(node);
730
- }
731
- });
732
- } else {
733
- removeChild(child);
650
+ if (this.key) {
651
+ componentCache.delete(this.key);
734
652
  }
735
653
  }
736
- // Method to inherit properties from another TemplateNode
737
654
  inheritNode(node) {
738
655
  this.mounted = node.mounted;
739
656
  this.nodes = node.nodes;
@@ -743,12 +660,11 @@ var TemplateNode = class {
743
660
  this.props = node.props;
744
661
  this.patchProps(props);
745
662
  }
746
- // Private method to map SSG node tree
747
663
  mapSSGNodeTree(parent) {
748
664
  this.treeMap.set(0, parent);
749
665
  this.walkNodeTree(parent, this.handleSSGNode.bind(this));
750
666
  }
751
- // Private method to map node tree
667
+ // protected method to map node tree
752
668
  mapNodeTree(parent, tree) {
753
669
  let index = 1;
754
670
  this.treeMap.set(0, parent);
@@ -759,7 +675,6 @@ var TemplateNode = class {
759
675
  };
760
676
  this.walkNodeTree(tree, handleNode);
761
677
  }
762
- // Private method to walk through the node tree
763
678
  walkNodeTree(node, handler) {
764
679
  if (node.nodeType !== Node.DOCUMENT_FRAGMENT_NODE) {
765
680
  handler(node);
@@ -770,7 +685,6 @@ var TemplateNode = class {
770
685
  child = child.nextSibling;
771
686
  }
772
687
  }
773
- // Private method to handle SSG nodes
774
688
  handleSSGNode(node) {
775
689
  var _a;
776
690
  if (node.nodeType === Node.COMMENT_NODE) {
@@ -786,7 +700,6 @@ var TemplateNode = class {
786
700
  }
787
701
  }
788
702
  }
789
- // Method to patch props onto the node
790
703
  patchProps(props) {
791
704
  if (!props) return;
792
705
  Object.entries(props).forEach(([key, value]) => {
@@ -798,15 +711,14 @@ var TemplateNode = class {
798
711
  });
799
712
  this.props = props;
800
713
  }
801
- // Private method to patch a single prop
802
714
  patchProp(key, node, props, isRoot) {
803
715
  if (!props) return;
804
716
  Object.entries(props).forEach(([attr, value]) => {
805
717
  if (attr === CHILDREN_PROP && value) {
806
718
  this.patchChildren(key, node, value, isRoot);
807
- } else if (attr === "ref") {
719
+ } else if (attr === REF_KEY) {
808
720
  props[attr].value = node;
809
- } else if (shared.startsWith(attr, "on")) {
721
+ } else if (shared.startsWith(attr, EVENT_PREFIX)) {
810
722
  this.patchEventListener(key, node, attr, value);
811
723
  } else {
812
724
  if (this.bindValueKeys.includes(attr)) return;
@@ -816,14 +728,12 @@ var TemplateNode = class {
816
728
  });
817
729
  }
818
730
  getBindUpdateValue(props, key, attr) {
819
- const UPDATE_PREFIX2 = "update";
820
- const updateKey = `${UPDATE_PREFIX2}${shared.capitalizeFirstLetter(attr)}`;
731
+ const updateKey = `${UPDATE_PREFIX}${shared.capitalize(attr)}`;
821
732
  if (updateKey && props[updateKey] && shared.isFunction(props[updateKey])) {
822
733
  this.bindValueKeys.push(updateKey);
823
734
  return props[updateKey];
824
735
  }
825
736
  }
826
- // Private method to patch children
827
737
  patchChildren(key, node, children, isRoot) {
828
738
  if (!shared.isArray(children)) {
829
739
  const trackKey = `${key}:${CHILDREN_PROP}:0`;
@@ -840,21 +750,19 @@ var TemplateNode = class {
840
750
  });
841
751
  }
842
752
  }
843
- // Private method to patch event listeners
844
753
  patchEventListener(key, node, attr, listener) {
845
754
  const eventName = attr.slice(2).toLowerCase();
846
755
  const track = this.getNodeTrack(`${key}:${attr}`);
847
756
  track.cleanup = addEventListener(node, eventName, listener);
848
757
  }
849
- // Private method to patch attributes
850
758
  patchAttribute(key, element, attr, value, updateFn) {
851
759
  const track = this.getNodeTrack(`${key}:${attr}`);
852
- const val = shared.isFunction(value) ? value() : value;
853
- const triggerValue = signal.isSignal(val) ? val : signal.useSignal(val);
854
- setAttribute(element, attr, triggerValue.value);
855
- const cleanup = signal.useEffect(() => {
856
- const val2 = shared.isFunction(value) ? value() : value;
857
- triggerValue.value = signal.isSignal(val2) ? val2.value : val2;
760
+ const triggerValue = signal.shallowSignal();
761
+ const cleanup = signal.effect(() => {
762
+ const unFnValue = shared.isFunction(value) ? value() : value;
763
+ if (shared.isPlainObject(unFnValue) && shared.isPlainObject(triggerValue.peek()) && JSON.stringify(triggerValue.value) === JSON.stringify(unFnValue))
764
+ return;
765
+ triggerValue.value = signal.isSignal(unFnValue) ? unFnValue.value : unFnValue;
858
766
  setAttribute(element, attr, triggerValue.value);
859
767
  });
860
768
  let cleanupBind;
@@ -868,9 +776,7 @@ var TemplateNode = class {
868
776
  cleanupBind && cleanupBind();
869
777
  };
870
778
  }
871
- // Private method to get or create a NodeTrack
872
779
  getNodeTrack(trackKey, trackLastNodes, isRoot) {
873
- var _a;
874
780
  let track = this.trackMap.get(trackKey);
875
781
  if (!track) {
876
782
  track = { cleanup: () => {
@@ -883,13 +789,12 @@ var TemplateNode = class {
883
789
  }
884
790
  this.trackMap.set(trackKey, track);
885
791
  }
886
- (_a = track.cleanup) == null ? void 0 : _a.call(track);
792
+ track.cleanup && track.cleanup();
887
793
  return track;
888
794
  }
889
- // Private method to patch a child node
890
795
  patchChild(track, parent, child, before) {
891
796
  if (shared.isFunction(child)) {
892
- track.cleanup = signal.useEffect(() => {
797
+ track.cleanup = signal.effect(() => {
893
798
  const nextNodes = shared.coerceArray(child()).map(coerceNode);
894
799
  if (renderContext.isSSR) {
895
800
  track.lastNodes = this.reconcileChildren(parent, nextNodes, before);
@@ -910,7 +815,6 @@ var TemplateNode = class {
910
815
  });
911
816
  }
912
817
  }
913
- // Private method to reconcile children nodes
914
818
  reconcileChildren(parent, nextNodes, before) {
915
819
  const result = /* @__PURE__ */ new Map();
916
820
  const textNodes = Array.from(parent.childNodes).filter(
@@ -936,19 +840,37 @@ var TemplateNode = class {
936
840
  }
937
841
  };
938
842
 
939
- // src/jsx-renderer.ts
940
- function h(template, props, key) {
941
- if (shared.isString(template)) {
942
- if (isHtmlTagName(template)) {
943
- const htmlTemplate = convertToHtmlTag(template);
944
- props = { [SINGLE_PROP_KEY]: props };
945
- return new TemplateNode(createTemplate(htmlTemplate), props, key);
946
- } else if (template === EMPTY_TEMPLATE) {
947
- props = { [FRAGMENT_PROP_KEY]: props };
948
- return new TemplateNode(createTemplate(EMPTY_TEMPLATE), props, key);
843
+ // src/jsxRenderer.ts
844
+ var componentCache = /* @__PURE__ */ new Map();
845
+ function createNodeCache(NodeConstructor, template, props = {}, key) {
846
+ if (key) {
847
+ const cached = componentCache.get(key);
848
+ if (cached) {
849
+ return cached;
949
850
  }
950
851
  }
951
- return shared.isFunction(template) ? new ComponentNode(template, props, key) : new TemplateNode(template, props, key);
852
+ if (typeof template === "string") {
853
+ template = createTemplate(template);
854
+ }
855
+ const newNode = new NodeConstructor(template, props, key);
856
+ if (key && newNode instanceof ComponentNode) {
857
+ componentCache.set(key, newNode);
858
+ }
859
+ return newNode;
860
+ }
861
+ function h(template, props = {}, key) {
862
+ if (template === EMPTY_TEMPLATE) {
863
+ return Fragment(template, props);
864
+ }
865
+ if (shared.isString(template)) {
866
+ const htmlTemplate = convertToHtmlTag(template);
867
+ const wrappedProps = { [SINGLE_PROP_KEY]: props };
868
+ return createNodeCache(TemplateNode, htmlTemplate, wrappedProps, key);
869
+ }
870
+ if (shared.isFunction(template)) {
871
+ return createNodeCache(ComponentNode, template, props, key);
872
+ }
873
+ return createNodeCache(TemplateNode, template, props, key);
952
874
  }
953
875
  function isComponent(node) {
954
876
  return node instanceof ComponentNode;
@@ -958,13 +880,17 @@ function isJsxElement(node) {
958
880
  }
959
881
  function createTemplate(html) {
960
882
  const template = document.createElement("template");
961
- template.innerHTML = closeHtmlTags(html);
883
+ template.innerHTML = html;
962
884
  return template;
963
885
  }
964
- function Fragment(props) {
965
- return h(EMPTY_TEMPLATE, {
966
- children: shared.isArray(props.children) ? props.children.filter(Boolean) : [props.children]
967
- });
886
+ function Fragment(template, props) {
887
+ const processedProps = props.children ? {
888
+ [FRAGMENT_PROP_KEY]: {
889
+ children: Array.isArray(props.children) ? props.children.filter(Boolean) : [props.children]
890
+ }
891
+ } : props;
892
+ const templateElement = template === EMPTY_TEMPLATE ? createTemplate(EMPTY_TEMPLATE) : template;
893
+ return createNodeCache(TemplateNode, templateElement, processedProps);
968
894
  }
969
895
  function onMount(cb) {
970
896
  assertInsideComponent("onMounted");
@@ -982,18 +908,15 @@ function assertInsideComponent(hookName, key) {
982
908
  );
983
909
  }
984
910
  }
985
- function useProvide(key, value) {
986
- assertInsideComponent("useProvide", key);
911
+ function provide(key, value) {
912
+ assertInsideComponent("provide", key);
987
913
  LifecycleContext.ref && LifecycleContext.ref.setContext(key, value);
988
914
  }
989
- function useInject(key, defaultValue) {
915
+ function inject(key, defaultValue) {
990
916
  var _a;
991
- assertInsideComponent("useInject", key);
917
+ assertInsideComponent("inject", key);
992
918
  return (_a = LifecycleContext.ref && LifecycleContext.ref.getContext(key)) != null ? _a : defaultValue;
993
919
  }
994
- function useRef() {
995
- return signal.shallowSignal(null);
996
- }
997
920
 
998
921
  // src/server.ts
999
922
  function renderToString(component, props) {
@@ -1022,13 +945,12 @@ function ssg(component, props) {
1022
945
  exports.Fragment = Fragment;
1023
946
  exports.h = h;
1024
947
  exports.hydrate = hydrate;
948
+ exports.inject = inject;
1025
949
  exports.isComponent = isComponent;
1026
950
  exports.isJsxElement = isJsxElement;
1027
951
  exports.onDestroy = onDestroy;
1028
952
  exports.onMount = onMount;
953
+ exports.provide = provide;
1029
954
  exports.renderToString = renderToString;
1030
955
  exports.ssg = ssg;
1031
956
  exports.template = createTemplate;
1032
- exports.useInject = useInject;
1033
- exports.useProvide = useProvide;
1034
- exports.useRef = useRef;