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