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