@estjs/template 0.0.13-beta.7 → 0.0.13

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,51 +1,66 @@
1
- import { isString, isFunction, isSymbol, isArray, escape, startsWith, isPrimitive, isNil, capitalizeFirstLetter, coerceArray, isFalsy, kebabCase } from '@estjs/shared';
1
+ import { isString, isFunction, isArray, isSymbol, escape, startsWith, isPrimitive, isNil, capitalizeFirstLetter, coerceArray, isFalsy, kebabCase } from '@estjs/shared';
2
2
  import { shallowSignal, isSignal, useReactive, useEffect, useSignal } from '@estjs/signal';
3
3
 
4
4
  /**
5
- * @estjs/template v0.0.13-beta.7
5
+ * @estjs/template v0.0.13
6
6
  * (c) 2023-Present jiangxd <jiangxd2016@gmail.com>
7
7
  * @license MIT
8
8
  **/
9
9
 
10
10
 
11
11
  // src/shared-config.ts
12
- var componentMap = /* @__PURE__ */ new Map();
12
+ var EVENT_PREFIX = "on";
13
+ var UPDATE_PREFIX = "update";
14
+ var CHILDREN_PROP = "children";
15
+ var EMPTY_TEMPLATE = "";
16
+ var FRAGMENT_PROP_KEY = "0";
17
+ var SINGLE_PROP_KEY = "1";
18
+ var PLACEHOLDER = " __PLACEHOLDER__ ";
13
19
  var RenderContext = class {
14
20
  constructor() {
15
21
  this.renderMode = 0 /* CLIENT */;
16
22
  }
23
+ // Getter to check if the current mode is SSG
17
24
  get isSSG() {
18
25
  return this.renderMode === 1 /* SSG */;
19
26
  }
27
+ // Getter to check if the current mode is SSR
20
28
  get isSSR() {
21
29
  return this.renderMode === 2 /* SSR */;
22
30
  }
31
+ // Getter to check if the current mode is Client
23
32
  get isClient() {
24
33
  return this.renderMode === 0 /* CLIENT */;
25
34
  }
35
+ // Set render mode to SSR
26
36
  setSSR() {
27
37
  this.renderMode = 2 /* SSR */;
28
38
  }
39
+ // Set render mode to SSG
29
40
  setSSG() {
30
41
  this.renderMode = 1 /* SSG */;
31
42
  }
43
+ // Set render mode to Client
32
44
  setClient() {
33
45
  this.renderMode = 0 /* CLIENT */;
34
46
  }
35
47
  };
36
48
  var renderContext = new RenderContext();
49
+ var componentMap = /* @__PURE__ */ new Map();
37
50
  function enterComponent(temp, index) {
38
51
  componentMap.set(temp, {
39
52
  index
40
53
  });
41
54
  }
42
55
  function getComponentIndex(temp) {
43
- return componentMap.get(temp).index;
56
+ var _a;
57
+ return (_a = componentMap.get(temp)) == null ? void 0 : _a.index;
44
58
  }
45
59
 
46
60
  // src/lifecycle-context.ts
47
61
  var _LifecycleContext = class _LifecycleContext {
48
62
  constructor() {
63
+ // Hooks for different lifecycle stages
49
64
  this.hooks = {
50
65
  mounted: /* @__PURE__ */ new Set(),
51
66
  destroy: /* @__PURE__ */ new Set()
@@ -55,28 +70,35 @@ var _LifecycleContext = class _LifecycleContext {
55
70
  }
56
71
  removeEventListener() {
57
72
  }
73
+ // Add a hook for a specific lifecycle stage
58
74
  addHook(hook, cb) {
59
75
  var _a;
60
76
  (_a = this.hooks[hook]) == null ? void 0 : _a.add(cb);
61
77
  }
78
+ // Get a value from the static context
62
79
  getContext(context) {
63
80
  return _LifecycleContext.context[context];
64
81
  }
82
+ // Set a value in the static context
65
83
  setContext(context, value) {
66
84
  _LifecycleContext.context[context] = value;
67
85
  }
86
+ // Initialize the static reference
68
87
  initRef() {
69
88
  _LifecycleContext.ref = this;
70
89
  }
90
+ // Remove the static reference
71
91
  removeRef() {
72
92
  _LifecycleContext.ref = null;
73
93
  }
94
+ // Clear all hooks
74
95
  clearHooks() {
75
96
  Object.values(this.hooks).forEach((set) => set.clear());
76
97
  }
77
98
  };
78
- // current context ref
99
+ // Static reference to the current context
79
100
  _LifecycleContext.ref = null;
101
+ // Static context to store shared values
80
102
  _LifecycleContext.context = {};
81
103
  var LifecycleContext = _LifecycleContext;
82
104
 
@@ -92,29 +114,39 @@ var SSGNode = class extends LifecycleContext {
92
114
  this.props = props;
93
115
  this.key = key;
94
116
  enterComponent(template, componentIndex);
117
+ this.templates = this.processTemplate();
118
+ }
119
+ // Process the template and return an array of processed strings
120
+ processTemplate() {
95
121
  if (isArray(this.template)) {
96
- const PLACEHOLDER = " __PLACEHOLDER__ ";
97
122
  const htmlString = this.template.join(PLACEHOLDER);
98
- const processedString = htmlString.replaceAll(/(<[^>]+>)|([^<]+)/g, (match, p1, p2) => {
99
- if (p1) {
100
- if (p1.includes("data-ci")) return match;
101
- return p1.replace(/<\s*([\da-z]+)(\s[^>]*)?>/i, (_, tagName, attrs) => {
102
- return `<${tagName} data-ci="${componentIndex}"${attrs || ""}>`;
103
- });
104
- } else if (p2 && p2.replace(PLACEHOLDER, "").trim()) {
105
- return `<!--${0 /* TEXT */}-${componentIndex}-->${p2}<!$>`;
106
- }
107
- return match;
108
- });
109
- this.template = processedString.split(PLACEHOLDER);
110
- }
123
+ const processedString = this.processHtmlString(htmlString);
124
+ return processedString.split(PLACEHOLDER);
125
+ }
126
+ return [];
127
+ }
128
+ // Process HTML string by adding component index and handling text nodes
129
+ processHtmlString(htmlString) {
130
+ return htmlString.replaceAll(/(<[^>]+>)|([^<]+)/g, (match, p1, p2) => {
131
+ if (p1) {
132
+ if (p1.includes("data-ci")) return match;
133
+ return p1.replace(/<\s*([\da-z]+)(\s[^>]*)?>/i, (_, tagName, attrs) => {
134
+ return `<${tagName} data-ci="${componentIndex}"${attrs || ""}>`;
135
+ });
136
+ } else if (p2 && p2.replace(PLACEHOLDER, "").trim()) {
137
+ return `<!--${0 /* TEXT */}-${componentIndex}-->${p2}<!$>`;
138
+ }
139
+ return match;
140
+ });
111
141
  }
142
+ // Mount the SSGNode and return the rendered string
112
143
  mount() {
113
144
  this.initRef();
114
145
  const output = this.render();
115
146
  this.removeRef();
116
147
  return output;
117
148
  }
149
+ // Render the SSGNode
118
150
  render() {
119
151
  if (isFunction(this.template)) {
120
152
  const root = this.template(this.props);
@@ -124,57 +156,66 @@ var SSGNode = class extends LifecycleContext {
124
156
  return String(root);
125
157
  }
126
158
  }
127
- const template = this.template;
128
- Object.keys(this.props).forEach((key) => {
129
- const cur = this.props[key];
130
- const childrens = cur.children;
131
- normalizeProp(cur);
132
- const findIndex = template.findIndex((t) => t.includes(`data-hk="${key}"`));
133
- if (childrens) {
134
- childrens.forEach(([child]) => {
135
- componentIndex++;
136
- const children = renderChildren(child, cur);
137
- this.template[findIndex] += children;
138
- });
159
+ return this.renderTemplate();
160
+ }
161
+ // Render the template by processing props and children
162
+ renderTemplate() {
163
+ Object.entries(this.props).forEach(([key, cur]) => {
164
+ const children = cur.children;
165
+ this.normalizeProps(cur);
166
+ const findIndex = this.templates.findIndex((t) => t.includes(`data-hk="${key}"`));
167
+ if (children) {
168
+ this.renderChildren(children, findIndex);
139
169
  }
140
- this.template[findIndex].replaceAll(
170
+ this.templates[findIndex] = this.templates[findIndex].replace(
141
171
  `data-hk="${key}"`,
142
- `data-hk="${key}" ${generateAttributes(cur)}`
172
+ `data-hk="${key}" ${this.generateAttributes(cur)}`
143
173
  );
144
174
  });
145
- return template.join("");
175
+ return this.templates.join("");
146
176
  }
147
- };
148
- function normalizeProp(props) {
149
- Object.entries(props).forEach(([key, value]) => {
150
- if (key === "children") {
151
- delete props[key];
152
- } else if (isFunction(value)) {
153
- delete props[key];
154
- } else if (isSignal(value)) {
155
- props[key] = value.value;
177
+ // Normalize props by removing children and handling signals
178
+ normalizeProps(props) {
179
+ Object.entries(props).forEach(([key, value]) => {
180
+ if (key === "children") {
181
+ delete props[key];
182
+ } else if (isFunction(value)) {
183
+ delete props[key];
184
+ } else if (isSignal(value)) {
185
+ props[key] = value.value;
186
+ }
187
+ });
188
+ }
189
+ // Generate HTML attributes string from props
190
+ generateAttributes(props) {
191
+ return Object.entries(props).filter(([key, value]) => key !== "children" && !isFunction(value)).map(([key, value]) => `${key}="${escape(String(value))}"`).join(" ");
192
+ }
193
+ // Render children and append them to the template
194
+ renderChildren(children, findIndex) {
195
+ children.forEach(([child]) => {
196
+ componentIndex++;
197
+ const renderedChild = this.renderChild(child);
198
+ this.templates[findIndex] += renderedChild;
199
+ });
200
+ }
201
+ // Render a single child node
202
+ renderChild(child) {
203
+ if (isFunction(child)) {
204
+ return this.renderChild(child(this.props));
205
+ } else if (isSignal(child)) {
206
+ return `<!--${1 /* TEXT_COMPONENT */}-${componentIndex}-->${child.value}<!$>`;
207
+ } else if (isSSGNode(child)) {
208
+ const childResult = child.mount();
209
+ return isFunction(childResult) ? childResult(this.props) : extractSignal(childResult);
210
+ } else {
211
+ return `<!--${1 /* TEXT_COMPONENT */}-${componentIndex}-->${child}<!$>`;
156
212
  }
157
- });
158
- }
159
- function generateAttributes(props) {
160
- return Object.entries(props).filter(([key, value]) => key !== "children" && !isFunction(value)).map(([key, value]) => `${key}="${escape(String(value))}"`).join(" ");
161
- }
162
- function renderChildren(children, prop) {
163
- if (isFunction(children)) {
164
- return renderChildren(children(prop), prop);
165
- } else if (isSignal(children)) {
166
- return `<!--${1 /* TEXT_COMPONENT */}-${componentIndex}-->${children.value}<!$>`;
167
- } else if (isSSGNode(children)) {
168
- const childResult = children.mount();
169
- return isFunction(childResult) ? childResult(prop) : extractSignal(childResult);
170
- } else {
171
- return `<!--${1 /* TEXT_COMPONENT */}-${componentIndex}-->${children}<!$>`;
172
213
  }
173
- }
214
+ };
174
215
 
175
216
  // src/utils.ts
176
- var selfClosingTags = "area,base,br,col,embed,hr,img,input,link,meta,param,source,track,wbr".split(",");
177
- var htmlTags = "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(
217
+ 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(
178
219
  ","
179
220
  );
180
221
  function coerceNode(data) {
@@ -211,26 +252,33 @@ function replaceChild(parent, node, child) {
211
252
  }
212
253
  function setAttribute(element, attr, value) {
213
254
  if (attr === "class") {
214
- if (typeof value === "string") {
215
- element.className = value;
216
- } else if (isArray(value)) {
217
- element.className = value.join(" ");
218
- } else if (value && typeof value === "object") {
219
- element.className = Object.entries(value).reduce((acc, [key, value2]) => acc + (value2 ? ` ${key}` : ""), "").trim();
220
- }
221
- return;
222
- }
223
- if (attr === "style") {
224
- if (typeof value === "string") {
225
- element.style.cssText = value;
226
- } else if (value && typeof value === "object") {
227
- const obj = value;
228
- Object.keys(obj).forEach((key) => {
229
- element.style.setProperty(kebabCase(key), String(obj[key]));
230
- });
231
- }
232
- return;
255
+ setClassAttribute(element, value);
256
+ } else if (attr === "style") {
257
+ setStyleAttribute(element, value);
258
+ } else {
259
+ setGenericAttribute(element, attr, value);
260
+ }
261
+ }
262
+ function setClassAttribute(element, value) {
263
+ if (typeof value === "string") {
264
+ element.className = value;
265
+ } else if (isArray(value)) {
266
+ element.className = value.join(" ");
267
+ } else if (value && typeof value === "object") {
268
+ element.className = Object.entries(value).reduce((acc, [key, value2]) => acc + (value2 ? ` ${key}` : ""), "").trim();
269
+ }
270
+ }
271
+ function setStyleAttribute(element, value) {
272
+ if (typeof value === "string") {
273
+ element.style.cssText = value;
274
+ } else if (value && typeof value === "object") {
275
+ const obj = value;
276
+ Object.entries(obj).forEach(([key, value2]) => {
277
+ element.style.setProperty(kebabCase(key), String(value2));
278
+ });
233
279
  }
280
+ }
281
+ function setGenericAttribute(element, attr, value) {
234
282
  if (isFalsy(value)) {
235
283
  element.removeAttribute(attr);
236
284
  } else if (value === true) {
@@ -313,7 +361,7 @@ function closeHtmlTags(input) {
313
361
  if (tagStack.length > 0) {
314
362
  tagStack.pop();
315
363
  }
316
- } else if (!selfClosingTags.includes(tagName)) {
364
+ } else if (!SELF_CLOSING_TAGS.includes(tagName)) {
317
365
  tagStack.push(tagName);
318
366
  }
319
367
  output.push(fullMatch);
@@ -328,14 +376,10 @@ function closeHtmlTags(input) {
328
376
  return output.join("");
329
377
  }
330
378
  function isHtmlTagName(tagName) {
331
- return htmlTags.includes(tagName);
379
+ return HTML_TAGS.includes(tagName);
332
380
  }
333
381
  function convertToHtmlTag(tagName) {
334
- if (selfClosingTags.includes(tagName)) {
335
- return `<${tagName}/>`;
336
- } else {
337
- return `<${tagName}></${tagName}>`;
338
- }
382
+ return SELF_CLOSING_TAGS.includes(tagName) ? `<${tagName}/>` : `<${tagName}></${tagName}>`;
339
383
  }
340
384
  function extractSignal(signal) {
341
385
  if (isSignal(signal)) {
@@ -353,19 +397,28 @@ var ComponentNode = class extends LifecycleContext {
353
397
  this.emitter = /* @__PURE__ */ new Set();
354
398
  this.rootNode = null;
355
399
  this.trackMap = /* @__PURE__ */ new Map();
356
- this.proxyProps = props ? useReactive(
400
+ this.key || (this.key = props == null ? void 0 : props.key);
401
+ this.proxyProps = this.createProxyProps(props);
402
+ }
403
+ // Create reactive props
404
+ createProxyProps(props) {
405
+ if (!props) return {};
406
+ return useReactive(
357
407
  props,
358
- (key2) => startsWith(key2, "on") || startsWith(key2, "update") || key2 === "children"
359
- ) : {};
408
+ (key) => startsWith(key, EVENT_PREFIX) || startsWith(key, UPDATE_PREFIX) || key === CHILDREN_PROP
409
+ );
360
410
  }
411
+ // Getter for the first child node
361
412
  get firstChild() {
362
413
  var _a, _b;
363
414
  return (_b = (_a = this.rootNode) == null ? void 0 : _a.firstChild) != null ? _b : null;
364
415
  }
416
+ // Getter to check if the node is connected to the DOM
365
417
  get isConnected() {
366
418
  var _a, _b;
367
419
  return (_b = (_a = this.rootNode) == null ? void 0 : _a.isConnected) != null ? _b : false;
368
420
  }
421
+ // Method to mount the component to the DOM
369
422
  mount(parent, before) {
370
423
  var _a, _b, _c, _d;
371
424
  if (!isFunction(this.template)) {
@@ -377,32 +430,37 @@ var ComponentNode = class extends LifecycleContext {
377
430
  this.initRef();
378
431
  this.rootNode = this.template(this.proxyProps);
379
432
  const mountedNode = (_d = (_c = this.rootNode) == null ? void 0 : _c.mount(parent, before)) != null ? _d : [];
380
- this.hooks.mounted.forEach((handler) => handler());
433
+ this.callMountHooks();
381
434
  this.patchProps(this.props);
382
435
  this.removeRef();
383
436
  return mountedNode;
384
437
  }
438
+ // Method to unmount the component from the DOM
385
439
  unmount() {
386
440
  var _a;
387
- this.hooks.destroy.forEach((handler) => handler());
441
+ this.callDestroyHooks();
388
442
  this.clearHooks();
389
443
  (_a = this.rootNode) == null ? void 0 : _a.unmount();
390
444
  this.rootNode = null;
391
445
  this.proxyProps = {};
446
+ this.clearEmitter();
447
+ }
448
+ // Private method to call mount hooks
449
+ callMountHooks() {
450
+ this.hooks.mounted.forEach((handler) => handler());
451
+ }
452
+ // Private method to call destroy hooks
453
+ callDestroyHooks() {
454
+ this.hooks.destroy.forEach((handler) => handler());
455
+ }
456
+ // Private method to clear the event emitter
457
+ clearEmitter() {
392
458
  for (const cleanup of this.emitter) {
393
459
  cleanup();
394
460
  }
395
461
  this.emitter.clear();
396
462
  }
397
- /**
398
- * Inherit props and state from another ComponentNode.
399
- * It will:
400
- * 1. Copy props from the node to this proxyProps.
401
- * 2. Copy the rootNode, trackMap and hooks from the node.
402
- * 3. Copy the props from the node to this.
403
- * 4. Patch props from the props passed in the constructor.
404
- * @param node The node to inherit from.
405
- */
463
+ // Method to inherit properties from another ComponentNode
406
464
  inheritNode(node) {
407
465
  Object.assign(this.proxyProps, node.proxyProps);
408
466
  this.rootNode = node.rootNode;
@@ -412,12 +470,7 @@ var ComponentNode = class extends LifecycleContext {
412
470
  this.props = node.props;
413
471
  this.patchProps(props);
414
472
  }
415
- /**
416
- * Get a NodeTrack from the trackMap. If the track is not in the trackMap, create a new one.
417
- * Then, call the cleanup function to remove any previously registered hooks.
418
- * @param trackKey the key of the node track to get.
419
- * @returns the NodeTrack, cleaned up and ready to use.
420
- */
473
+ // Private method to get or create a NodeTrack
421
474
  getNodeTrack(trackKey) {
422
475
  let track = this.trackMap.get(trackKey);
423
476
  if (!track) {
@@ -428,39 +481,47 @@ var ComponentNode = class extends LifecycleContext {
428
481
  track.cleanup();
429
482
  return track;
430
483
  }
431
- /**
432
- * Patch the props of this node.
433
- * It will:
434
- * 1. Iterate the props and patch it.
435
- * 2. If the prop is a event handler, add a event listener to the first child of the node.
436
- * 3. If the prop is a ref, set the first child of the node to the ref.
437
- * 4. If the prop is a update handler, update the prop in the node's props.
438
- * 5. If the prop is a normal prop, create a signal for it and then patch it.
439
- * @param props The props to patch.
440
- */
484
+ // Method to patch props onto the component
441
485
  patchProps(props) {
442
- var _a, _b;
486
+ var _a;
443
487
  if (!props) {
444
488
  return;
445
489
  }
446
490
  for (const [key, prop] of Object.entries(props)) {
447
- if (startsWith(key, "on") && ((_a = this.rootNode) == null ? void 0 : _a.firstChild)) {
448
- const event = key.slice(2).toLowerCase();
449
- const cleanup = addEventListener(this.rootNode.nodes[0], event, prop);
450
- this.emitter.add(cleanup);
491
+ if (startsWith(key, EVENT_PREFIX) && ((_a = this.rootNode) == null ? void 0 : _a.firstChild)) {
492
+ this.patchEventListener(key, prop);
451
493
  } else if (key === "ref") {
452
- prop.value = (_b = this.rootNode) == null ? void 0 : _b.firstChild;
453
- } else if (startsWith(key, "update")) {
454
- this.props[key] = extractSignal(prop);
455
- } else if (key !== "children") {
456
- const track = this.getNodeTrack(key);
457
- track.cleanup = useEffect(() => {
458
- this.proxyProps[key] = isFunction(prop) ? prop() : prop;
459
- });
494
+ this.patchRef(prop);
495
+ } else if (startsWith(key, UPDATE_PREFIX)) {
496
+ this.patchUpdateHandler(key, prop);
497
+ } else if (key !== CHILDREN_PROP) {
498
+ this.patchNormalProp(key, prop);
460
499
  }
461
500
  }
462
501
  this.props = props;
463
502
  }
503
+ // Private method to patch event listeners
504
+ patchEventListener(key, prop) {
505
+ const event = key.slice(2).toLowerCase();
506
+ const cleanup = addEventListener(this.rootNode.nodes[0], event, prop);
507
+ this.emitter.add(cleanup);
508
+ }
509
+ // Private method to patch ref
510
+ patchRef(prop) {
511
+ var _a, _b;
512
+ prop.value = (_b = (_a = this.rootNode) == null ? void 0 : _a.firstChild) != null ? _b : null;
513
+ }
514
+ // Private method to patch update handlers
515
+ patchUpdateHandler(key, prop) {
516
+ this.props[key] = extractSignal(prop);
517
+ }
518
+ // Private method to patch normal props
519
+ patchNormalProp(key, prop) {
520
+ const track = this.getNodeTrack(key);
521
+ track.cleanup = useEffect(() => {
522
+ this.proxyProps[key] = isFunction(prop) ? prop() : prop;
523
+ });
524
+ }
464
525
  };
465
526
 
466
527
  // src/patch.ts
@@ -576,6 +637,7 @@ var TemplateNode = class {
576
637
  this.template = template;
577
638
  this.props = props;
578
639
  this.key = key;
640
+ // Private properties for managing the node's state
579
641
  this.treeMap = /* @__PURE__ */ new Map();
580
642
  this.mounted = false;
581
643
  this.nodes = [];
@@ -587,18 +649,21 @@ var TemplateNode = class {
587
649
  this.componentIndex = getComponentIndex(this.template);
588
650
  }
589
651
  }
590
- addEventListener() {
591
- }
592
- removeEventListener() {
593
- }
652
+ // Getter for the first child node
594
653
  get firstChild() {
595
654
  var _a;
596
655
  return (_a = this.nodes[0]) != null ? _a : null;
597
656
  }
598
- // is mounted
657
+ // Getter to check if the node is connected to the DOM
599
658
  get isConnected() {
600
659
  return this.mounted;
601
660
  }
661
+ // Placeholder methods for event handling
662
+ addEventListener() {
663
+ }
664
+ removeEventListener() {
665
+ }
666
+ // Method to mount the node to the DOM
602
667
  mount(parent, before) {
603
668
  var _a;
604
669
  this.parent = parent;
@@ -628,6 +693,7 @@ var TemplateNode = class {
628
693
  this.mounted = true;
629
694
  return this.nodes;
630
695
  }
696
+ // Method to unmount the node from the DOM
631
697
  unmount() {
632
698
  var _a, _b;
633
699
  this.trackMap.forEach((track) => {
@@ -638,34 +704,34 @@ var TemplateNode = class {
638
704
  this.treeMap.clear();
639
705
  this.nodes.forEach((node) => removeChild(node));
640
706
  if (!this.template.innerHTML && !this.nodes.length) {
641
- ((_b = (_a = this.props) == null ? void 0 : _a[0]) == null ? void 0 : _b.children) && this.props[0].children.forEach((i) => {
642
- var _a2;
643
- if (isPrimitive(i)) {
644
- (_a2 = this.parent) == null ? void 0 : _a2.childNodes.forEach((node) => {
645
- var _a3;
646
- if (node.nodeType === Node.TEXT_NODE && node.textContent === `${i}`) {
647
- (_a3 = this.parent) == null ? void 0 : _a3.removeChild(node);
648
- }
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);
649
712
  });
650
713
  } else {
651
- removeChild(i);
714
+ this.deleteFragmentTextNode(children);
652
715
  }
653
- });
716
+ }
654
717
  }
655
718
  this.nodes = [];
656
719
  this.mounted = false;
657
720
  }
658
- patchProps(props) {
659
- if (!props) return;
660
- Object.entries(props).forEach(([key, value]) => {
661
- const index = Number(key);
662
- const node = this.treeMap.get(index);
663
- if (node) {
664
- this.patchProp(key, node, value, index === 0);
665
- }
666
- });
667
- this.props = props;
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);
732
+ }
668
733
  }
734
+ // Method to inherit properties from another TemplateNode
669
735
  inheritNode(node) {
670
736
  this.mounted = node.mounted;
671
737
  this.nodes = node.nodes;
@@ -675,55 +741,129 @@ var TemplateNode = class {
675
741
  this.props = node.props;
676
742
  this.patchProps(props);
677
743
  }
744
+ // Private method to map SSG node tree
678
745
  mapSSGNodeTree(parent) {
679
746
  this.treeMap.set(0, parent);
680
- const walk = (node) => {
681
- var _a;
682
- if (node.nodeType !== Node.DOCUMENT_FRAGMENT_NODE) {
683
- if (node.nodeType === Node.COMMENT_NODE) {
684
- const [type, index] = ((_a = node.textContent) == null ? void 0 : _a.split("-")) || "";
685
- if (0 /* TEXT */ === +type && +index === this.componentIndex) {
686
- const textNode = node.nextSibling;
687
- this.treeMap.set(+index, textNode);
688
- }
689
- } else if (node.nodeType !== Node.TEXT_NODE) {
690
- const { ci = "-1", hk } = (node == null ? void 0 : node.dataset) || {};
691
- if (hk && +ci === this.componentIndex) {
692
- this.treeMap.set(+hk, node);
693
- }
694
- }
695
- }
696
- let child = node.firstChild;
697
- while (child) {
698
- walk(child);
699
- child = child.nextSibling;
700
- }
701
- };
702
- walk(parent);
747
+ this.walkNodeTree(parent, this.handleSSGNode.bind(this));
703
748
  }
749
+ // Private method to map node tree
704
750
  mapNodeTree(parent, tree) {
705
751
  let index = 1;
706
752
  this.treeMap.set(0, parent);
707
- const walk = (node) => {
753
+ const handleNode = (node) => {
708
754
  if (node.nodeType !== Node.DOCUMENT_FRAGMENT_NODE) {
709
755
  this.treeMap.set(index++, node);
710
756
  }
711
- let child = node.firstChild;
712
- while (child) {
713
- walk(child);
714
- child = child.nextSibling;
757
+ };
758
+ this.walkNodeTree(tree, handleNode);
759
+ }
760
+ // Private method to walk through the node tree
761
+ walkNodeTree(node, handler) {
762
+ if (node.nodeType !== Node.DOCUMENT_FRAGMENT_NODE) {
763
+ handler(node);
764
+ }
765
+ let child = node.firstChild;
766
+ while (child) {
767
+ this.walkNodeTree(child, handler);
768
+ child = child.nextSibling;
769
+ }
770
+ }
771
+ // Private method to handle SSG nodes
772
+ handleSSGNode(node) {
773
+ var _a;
774
+ if (node.nodeType === Node.COMMENT_NODE) {
775
+ const [type, index] = ((_a = node.textContent) == null ? void 0 : _a.split("-")) || [];
776
+ if (0 /* TEXT */ === +type && +index === this.componentIndex) {
777
+ const textNode = node.nextSibling;
778
+ this.treeMap.set(+index, textNode);
779
+ }
780
+ } else if (node.nodeType !== Node.TEXT_NODE) {
781
+ const { ci = "-1", hk } = (node == null ? void 0 : node.dataset) || {};
782
+ if (hk && +ci === this.componentIndex) {
783
+ this.treeMap.set(+hk, node);
715
784
  }
785
+ }
786
+ }
787
+ // Method to patch props onto the node
788
+ patchProps(props) {
789
+ if (!props) return;
790
+ Object.entries(props).forEach(([key, value]) => {
791
+ const index = Number(key);
792
+ const node = this.treeMap.get(index);
793
+ if (node) {
794
+ this.patchProp(key, node, value, index === 0);
795
+ }
796
+ });
797
+ this.props = props;
798
+ }
799
+ // Private method to patch a single prop
800
+ patchProp(key, node, props, isRoot) {
801
+ if (!props) return;
802
+ Object.entries(props).forEach(([attr, value]) => {
803
+ if (attr === CHILDREN_PROP && value) {
804
+ this.patchChildren(key, node, value, isRoot);
805
+ } else if (attr === "ref") {
806
+ props[attr].value = node;
807
+ } else if (startsWith(attr, "on")) {
808
+ this.patchEventListener(key, node, attr, value);
809
+ } else {
810
+ this.patchAttribute(key, node, attr, value);
811
+ }
812
+ });
813
+ }
814
+ // Private method to patch children
815
+ patchChildren(key, node, children, isRoot) {
816
+ if (!isArray(children)) {
817
+ const trackKey = `${key}:${CHILDREN_PROP}:0`;
818
+ const track = this.getNodeTrack(trackKey, true, isRoot);
819
+ this.patchChild(track, node, children, null);
820
+ } else {
821
+ children.filter(Boolean).forEach((item, index) => {
822
+ var _a;
823
+ const [child, path] = isArray(item) ? item : [item, null];
824
+ const before = isNil(path) ? null : (_a = this.treeMap.get(path)) != null ? _a : null;
825
+ const trackKey = `${key}:${CHILDREN_PROP}:${index}`;
826
+ const track = this.getNodeTrack(trackKey, true, isRoot);
827
+ this.patchChild(track, node, child, before);
828
+ });
829
+ }
830
+ }
831
+ // Private method to patch event listeners
832
+ patchEventListener(key, node, attr, listener) {
833
+ const eventName = attr.slice(2).toLowerCase();
834
+ const track = this.getNodeTrack(`${key}:${attr}`);
835
+ track.cleanup = addEventListener(node, eventName, listener);
836
+ }
837
+ // Private method to patch attributes
838
+ patchAttribute(key, element, attr, value) {
839
+ var _a, _b;
840
+ const updateKey = `update${capitalizeFirstLetter(attr)}`;
841
+ if (this.bindValueKeys.includes(attr)) {
842
+ return;
843
+ }
844
+ if ((_a = this.props) == null ? void 0 : _a[updateKey]) {
845
+ this.bindValueKeys.push(updateKey);
846
+ }
847
+ const track = this.getNodeTrack(`${key}:${attr}`);
848
+ const triggerValue = isSignal(value) ? value : useSignal(value);
849
+ setAttribute(element, attr, triggerValue.value);
850
+ const cleanup = useEffect(() => {
851
+ triggerValue.value = isSignal(value) ? value.value : value;
852
+ setAttribute(element, attr, triggerValue.value);
853
+ });
854
+ let cleanupBind;
855
+ if (((_b = this.props) == null ? void 0 : _b[updateKey]) && !isComponent(attr)) {
856
+ cleanupBind = bindNode(element, (value2) => {
857
+ var _a2;
858
+ (_a2 = this.props) == null ? void 0 : _a2[updateKey](value2);
859
+ });
860
+ }
861
+ track.cleanup = () => {
862
+ cleanup && cleanup();
863
+ cleanupBind && cleanupBind();
716
864
  };
717
- walk(tree);
718
- }
719
- /**
720
- * Get a NodeTrack from the trackMap. If the track is not in the trackMap, create a new one.
721
- * Then, call the cleanup function to remove any previously registered hooks.
722
- * @param trackKey the key of the node track to get.
723
- * @param trackLastNodes if true, the track will record the last nodes it has rendered.
724
- * @param isRoot if true, the track will be treated as a root track.
725
- * @returns the NodeTrack, cleaned up and ready to use.
726
- */
865
+ }
866
+ // Private method to get or create a NodeTrack
727
867
  getNodeTrack(trackKey, trackLastNodes, isRoot) {
728
868
  var _a;
729
869
  let track = this.trackMap.get(trackKey);
@@ -741,130 +881,67 @@ var TemplateNode = class {
741
881
  (_a = track.cleanup) == null ? void 0 : _a.call(track);
742
882
  return track;
743
883
  }
744
- patchProp(key, node, props, isRoot) {
745
- for (const attr in props) {
746
- if (attr === "children" && props.children) {
747
- if (!isArray(props.children)) {
748
- const trackKey = `${key}:${attr}:${0}`;
749
- const track = this.getNodeTrack(trackKey, true, isRoot);
750
- patchChild(track, node, props.children, null);
884
+ // Private method to patch a child node
885
+ patchChild(track, parent, child, before) {
886
+ if (isFunction(child)) {
887
+ track.cleanup = useEffect(() => {
888
+ const nextNodes = coerceArray(child()).map(coerceNode);
889
+ if (renderContext.isSSR) {
890
+ track.lastNodes = this.reconcileChildren(parent, nextNodes, before);
751
891
  } else {
752
- props.children.filter(Boolean).forEach((item, index) => {
753
- var _a;
754
- const [child, path] = isArray(item) ? item : [item, null];
755
- const before = isNil(path) ? null : (_a = this.treeMap.get(path)) != null ? _a : null;
756
- const trackKey = `${key}:${attr}:${index}`;
757
- const track = this.getNodeTrack(trackKey, true, isRoot);
758
- patchChild(track, node, child, before);
759
- });
892
+ track.lastNodes = patchChildren(parent, track.lastNodes, nextNodes, before);
760
893
  }
761
- } else if (attr === "ref") {
762
- props[attr].value = node;
763
- } else if (startsWith(attr, "on")) {
764
- const eventName = attr.slice(2).toLocaleLowerCase();
765
- const track = this.getNodeTrack(`${key}:${attr}`);
766
- const listener = props[attr];
767
- track.cleanup = addEventListener(node, eventName, listener);
768
- } else {
769
- const updateKey = `update${capitalizeFirstLetter(attr)}`;
770
- if (this.bindValueKeys.includes(attr)) {
771
- break;
772
- }
773
- if (props[updateKey]) {
774
- this.bindValueKeys.push(updateKey);
775
- }
776
- const track = this.getNodeTrack(`${key}:${attr}`);
777
- const val = props[attr];
778
- const triggerValue = isSignal(val) ? val : useSignal(val);
779
- patchAttribute(track, node, attr, triggerValue.value);
780
- const cleanup = useEffect(() => {
781
- triggerValue.value = isSignal(val) ? val.value : val;
782
- patchAttribute(track, node, attr, triggerValue.value);
783
- });
784
- let cleanupBind;
785
- if (props[updateKey] && !isComponent(attr)) {
786
- cleanupBind = bindNode(node, (value) => {
787
- props[updateKey](value);
788
- });
894
+ });
895
+ } else {
896
+ coerceArray(child).forEach((node, index) => {
897
+ const newNode = coerceNode(node);
898
+ const key = getKey(newNode, index);
899
+ if (renderContext.isSSR) {
900
+ track.lastNodes = this.reconcileChildren(parent, [newNode], before);
901
+ } else {
902
+ track.lastNodes.set(key, newNode);
903
+ insertChild(parent, newNode, before);
789
904
  }
790
- track.cleanup = () => {
791
- cleanup && cleanup();
792
- cleanupBind && cleanupBind();
793
- };
794
- }
905
+ });
795
906
  }
796
907
  }
797
- };
798
- function patchChild(track, parent, child, before) {
799
- if (isFunction(child)) {
800
- track.cleanup = useEffect(() => {
801
- const nextNodes = coerceArray(child()).map(coerceNode);
802
- if (renderContext.isSSR) {
803
- track.lastNodes = reconcileChildren(parent, nextNodes, before);
804
- } else {
805
- track.lastNodes = patchChildren(parent, track.lastNodes, nextNodes, before);
908
+ // Private method to reconcile children nodes
909
+ reconcileChildren(parent, nextNodes, before) {
910
+ const result = /* @__PURE__ */ new Map();
911
+ const textNodes = Array.from(parent.childNodes).filter(
912
+ (node) => {
913
+ var _a, _b;
914
+ return node.nodeType === Node.TEXT_NODE && ((_a = node.previousSibling) == null ? void 0 : _a.nodeType) === Node.COMMENT_NODE && ((_b = node.nextSibling) == null ? void 0 : _b.nodeType) === Node.COMMENT_NODE;
806
915
  }
807
- });
808
- } else {
809
- coerceArray(child).forEach((node, index) => {
810
- const newNode = coerceNode(node);
811
- const key = getKey(newNode, index);
812
- if (renderContext.isSSR) {
813
- track.lastNodes = reconcileChildren(parent, [newNode], before);
916
+ );
917
+ nextNodes.forEach((node, index) => {
918
+ const key = getKey(node, index);
919
+ if (node.nodeType === Node.TEXT_NODE) {
920
+ textNodes.forEach((ne) => {
921
+ if (node.textContent === ne.textContent) {
922
+ parent.replaceChild(node, ne);
923
+ }
924
+ });
814
925
  } else {
815
- track.lastNodes.set(key, newNode);
816
- insertChild(parent, newNode, before);
926
+ insertChild(parent, node, before);
817
927
  }
928
+ result.set(key, node);
818
929
  });
930
+ return result;
819
931
  }
820
- }
821
- function reconcileChildren(parent, nextNodes, before) {
822
- const result = /* @__PURE__ */ new Map();
823
- const textNodes = Array.from(parent.childNodes).filter(
824
- (node) => {
825
- var _a, _b;
826
- return node.nodeType === Node.TEXT_NODE && ((_a = node.previousSibling) == null ? void 0 : _a.nodeType) === Node.COMMENT_NODE && ((_b = node.nextSibling) == null ? void 0 : _b.nodeType) === Node.COMMENT_NODE;
827
- }
828
- );
829
- nextNodes.forEach((node, index) => {
830
- const key = getKey(node, index);
831
- if (node.nodeType === Node.TEXT_NODE) {
832
- textNodes.forEach((ne) => {
833
- if (node.textContent === ne.textContent) {
834
- parent.replaceChild(node, ne);
835
- }
836
- });
837
- } else {
838
- insertChild(parent, node, before);
839
- }
840
- result.set(key, node);
841
- });
842
- return result;
843
- }
844
- function patchAttribute(track, node, attr, data) {
845
- const element = node;
846
- if (!element.setAttribute) {
847
- return;
848
- }
849
- if (isFunction(data)) {
850
- track.cleanup = useEffect(() => {
851
- setAttribute(element, attr, data());
852
- });
853
- } else {
854
- setAttribute(element, attr, data);
855
- }
856
- }
932
+ };
857
933
 
858
934
  // src/jsx-renderer.ts
859
935
  function h(template, props, key) {
860
936
  if (isString(template)) {
861
937
  if (isHtmlTagName(template)) {
862
- template = convertToHtmlTag(template);
863
- props = { "1": props };
864
- } else if (template === "") {
865
- props = { "0": props };
938
+ const htmlTemplate = convertToHtmlTag(template);
939
+ props = { [SINGLE_PROP_KEY]: props };
940
+ return new TemplateNode(createTemplate(htmlTemplate), props, key);
941
+ } else if (template === EMPTY_TEMPLATE) {
942
+ props = { [FRAGMENT_PROP_KEY]: props };
943
+ return new TemplateNode(createTemplate(EMPTY_TEMPLATE), props, key);
866
944
  }
867
- template = createTemplate(template);
868
945
  }
869
946
  return isFunction(template) ? new ComponentNode(template, props, key) : new TemplateNode(template, props, key);
870
947
  }
@@ -880,19 +957,17 @@ function createTemplate(html) {
880
957
  return template;
881
958
  }
882
959
  function Fragment(props) {
883
- return h("", {
884
- children: Array.isArray(props.children) ? props.children.filter(Boolean) : [props.children]
960
+ return h(EMPTY_TEMPLATE, {
961
+ children: isArray(props.children) ? props.children.filter(Boolean) : [props.children]
885
962
  });
886
963
  }
887
964
  function onMount(cb) {
888
- var _a;
889
965
  assertInsideComponent("onMounted");
890
- (_a = LifecycleContext.ref) == null ? void 0 : _a.addHook("mounted", cb);
966
+ LifecycleContext.ref && LifecycleContext.ref.addHook("mounted", cb);
891
967
  }
892
968
  function onDestroy(cb) {
893
- var _a;
894
969
  assertInsideComponent("onDestroy");
895
- (_a = LifecycleContext.ref) == null ? void 0 : _a.addHook("destroy", cb);
970
+ LifecycleContext.ref && LifecycleContext.ref.addHook("destroy", cb);
896
971
  }
897
972
  function assertInsideComponent(hookName, key) {
898
973
  if (!LifecycleContext.ref && true) {
@@ -903,21 +978,19 @@ function assertInsideComponent(hookName, key) {
903
978
  }
904
979
  }
905
980
  function useProvide(key, value) {
906
- var _a;
907
981
  assertInsideComponent("useProvide", key);
908
- (_a = LifecycleContext.ref) == null ? void 0 : _a.setContext(key, value);
982
+ LifecycleContext.ref && LifecycleContext.ref.setContext(key, value);
909
983
  }
910
984
  function useInject(key, defaultValue) {
911
985
  var _a;
912
986
  assertInsideComponent("useInject", key);
913
- return ((_a = LifecycleContext.ref) == null ? void 0 : _a.getContext(key)) || defaultValue;
987
+ return (_a = LifecycleContext.ref && LifecycleContext.ref.getContext(key)) != null ? _a : defaultValue;
914
988
  }
915
989
  function useRef() {
916
- const ref = shallowSignal(null);
917
- return ref;
990
+ return shallowSignal(null);
918
991
  }
919
992
 
920
- // src/hydration.ts
993
+ // src/server.ts
921
994
  function renderToString(component, props) {
922
995
  renderContext.setSSG();
923
996
  const ssrNode = new SSGNode(component, props || {});