sinwan 0.1.0 → 1.0.0

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.
Files changed (87) hide show
  1. package/README.md +66 -39
  2. package/dist/cjs/index.development.js +1624 -961
  3. package/dist/cjs/index.development.js.map +21 -18
  4. package/dist/cjs/index.production.min.js +2 -2
  5. package/dist/cjs/index.production.min.js.map +21 -18
  6. package/dist/cjs/jsx/jsx-dev-runtime.development.js +6 -16
  7. package/dist/cjs/jsx/jsx-dev-runtime.development.js.map +3 -3
  8. package/dist/cjs/jsx/jsx-dev-runtime.production.min.js +2 -2
  9. package/dist/cjs/jsx/jsx-dev-runtime.production.min.js.map +3 -3
  10. package/dist/cjs/jsx/jsx-runtime.development.js +6 -16
  11. package/dist/cjs/jsx/jsx-runtime.development.js.map +3 -3
  12. package/dist/cjs/jsx/jsx-runtime.production.min.js +2 -2
  13. package/dist/cjs/jsx/jsx-runtime.production.min.js.map +3 -3
  14. package/dist/cjs/renderer/index.development.js +1175 -0
  15. package/dist/cjs/renderer/index.development.js.map +24 -0
  16. package/dist/cjs/renderer/index.production.min.js +3 -0
  17. package/dist/cjs/renderer/index.production.min.js.map +24 -0
  18. package/dist/cjs/server/index.development.js +665 -329
  19. package/dist/cjs/server/index.development.js.map +11 -10
  20. package/dist/cjs/server/index.production.min.js +2 -2
  21. package/dist/cjs/server/index.production.min.js.map +11 -10
  22. package/dist/component/control-flow.d.ts +18 -0
  23. package/dist/component/control-flow.d.ts.map +1 -0
  24. package/dist/component/index.d.ts +3 -1
  25. package/dist/component/index.d.ts.map +1 -1
  26. package/dist/component/instance.d.ts +7 -1
  27. package/dist/component/instance.d.ts.map +1 -1
  28. package/dist/component/lifecycle.d.ts +2 -1
  29. package/dist/component/lifecycle.d.ts.map +1 -1
  30. package/dist/component/provide-inject.d.ts +11 -5
  31. package/dist/component/provide-inject.d.ts.map +1 -1
  32. package/dist/esm/index.development.js +1301 -660
  33. package/dist/esm/index.development.js.map +21 -18
  34. package/dist/esm/index.production.min.js +2 -2
  35. package/dist/esm/index.production.min.js.map +21 -18
  36. package/dist/esm/jsx/jsx-dev-runtime.development.js +6 -16
  37. package/dist/esm/jsx/jsx-dev-runtime.development.js.map +3 -3
  38. package/dist/esm/jsx/jsx-dev-runtime.production.min.js +2 -2
  39. package/dist/esm/jsx/jsx-dev-runtime.production.min.js.map +3 -3
  40. package/dist/esm/jsx/jsx-runtime.development.js +6 -16
  41. package/dist/esm/jsx/jsx-runtime.development.js.map +3 -3
  42. package/dist/esm/jsx/jsx-runtime.production.min.js +2 -2
  43. package/dist/esm/jsx/jsx-runtime.production.min.js.map +3 -3
  44. package/dist/esm/renderer/index.development.js +1124 -0
  45. package/dist/esm/renderer/index.development.js.map +24 -0
  46. package/dist/esm/renderer/index.production.min.js +4 -0
  47. package/dist/esm/renderer/index.production.min.js.map +24 -0
  48. package/dist/esm/server/index.development.js +665 -329
  49. package/dist/esm/server/index.development.js.map +11 -10
  50. package/dist/esm/server/index.production.min.js +2 -2
  51. package/dist/esm/server/index.production.min.js.map +11 -10
  52. package/dist/hydration/walk.d.ts.map +1 -1
  53. package/dist/index.d.ts +6 -6
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/jsx/jsx-runtime.d.ts +13 -0
  56. package/dist/jsx/jsx-runtime.d.ts.map +1 -1
  57. package/dist/jsx/jsx-types.d.ts +122 -57
  58. package/dist/jsx/jsx-types.d.ts.map +1 -1
  59. package/dist/renderer/attributes.d.ts.map +1 -1
  60. package/dist/renderer/dom-ops.d.ts +4 -1
  61. package/dist/renderer/dom-ops.d.ts.map +1 -1
  62. package/dist/renderer/index.d.ts +1 -1
  63. package/dist/renderer/index.d.ts.map +1 -1
  64. package/dist/renderer/mount.d.ts +2 -5
  65. package/dist/renderer/mount.d.ts.map +1 -1
  66. package/dist/renderer/render-children.d.ts +2 -2
  67. package/dist/renderer/render-children.d.ts.map +1 -1
  68. package/dist/renderer/render-control-flow.d.ts +13 -0
  69. package/dist/renderer/render-control-flow.d.ts.map +1 -0
  70. package/dist/renderer/render-element.d.ts +1 -1
  71. package/dist/renderer/render-element.d.ts.map +1 -1
  72. package/dist/renderer/types.d.ts +2 -0
  73. package/dist/renderer/types.d.ts.map +1 -1
  74. package/dist/renderer/unmount.d.ts +20 -0
  75. package/dist/renderer/unmount.d.ts.map +1 -0
  76. package/dist/renderer.d.ts +1 -0
  77. package/dist/renderer.js +7 -0
  78. package/dist/renderer.mjs +4 -0
  79. package/dist/server/hydration-markers.d.ts.map +1 -1
  80. package/dist/server/index.d.ts +1 -1
  81. package/dist/server/index.d.ts.map +1 -1
  82. package/dist/server/renderer.d.ts.map +1 -1
  83. package/dist/server/stream.d.ts +9 -1
  84. package/dist/server/stream.d.ts.map +1 -1
  85. package/dist/types.d.ts +8 -2
  86. package/dist/types.d.ts.map +1 -1
  87. package/package.json +15 -1
@@ -40,22 +40,12 @@ function buildElement(type, props, children) {
40
40
  if (type === Fragment) {
41
41
  return { tag: "", props: {}, children };
42
42
  }
43
- if (typeof type === "function") {
44
- if (type._SinwanComponent || type._SinwanPage) {
45
- const result2 = type(props);
46
- if (result2 && typeof result2 === "object" && "tag" in result2) {
47
- return result2;
48
- }
49
- return { tag: "", props: {}, children: normalizeChildren(result2) };
50
- }
51
- const result = type(props);
52
- if (result && typeof result === "object" && "tag" in result) {
53
- return result;
43
+ if (typeof type === "function" || typeof type === "string") {
44
+ const finalProps = props ?? {};
45
+ if (children.length > 0 && finalProps.children === undefined) {
46
+ finalProps.children = children.length === 1 ? children[0] : children;
54
47
  }
55
- return { tag: "", props: {}, children: normalizeChildren(result) };
56
- }
57
- if (typeof type === "string") {
58
- return { tag: type, props: props || {}, children };
48
+ return { tag: type, props: finalProps, children };
59
49
  }
60
50
  return { tag: "", props: {}, children };
61
51
  }
@@ -109,221 +99,6 @@ function isSafeHtml(value) {
109
99
  return value instanceof HtmlEscapedString;
110
100
  }
111
101
 
112
- // src/server/renderer.ts
113
- var componentCache = new WeakMap;
114
- var pageRegistry = new Map;
115
- function registerPage(name, page) {
116
- pageRegistry.set(name, page);
117
- }
118
- function getPage(name) {
119
- return pageRegistry.get(name);
120
- }
121
- function hasPage(name) {
122
- return pageRegistry.has(name);
123
- }
124
- async function renderPage(name, data) {
125
- const page = getPage(name);
126
- if (!page) {
127
- throw new Error(`Page "${name}" not found in registry`);
128
- }
129
- const element = await page(data);
130
- return renderToString(element);
131
- }
132
- async function renderToString(node) {
133
- if (node == null || typeof node === "boolean") {
134
- return "";
135
- }
136
- if (typeof node === "string") {
137
- return escapeHtml(node);
138
- }
139
- if (typeof node === "number") {
140
- return String(node);
141
- }
142
- if (node instanceof HtmlEscapedString) {
143
- return node.value;
144
- }
145
- if (Array.isArray(node)) {
146
- const results = await Promise.all(node.map((child) => renderToString(child)));
147
- return results.join("");
148
- }
149
- if (node instanceof Promise) {
150
- return renderElement(await node);
151
- }
152
- return renderElement(node);
153
- }
154
- async function renderElement(element) {
155
- const { tag, props, children } = element;
156
- if (typeof tag === "function") {
157
- const result = await tag(props);
158
- return renderToString(result);
159
- }
160
- if (typeof tag === "string") {
161
- return renderIntrinsicElement(tag, props, children);
162
- }
163
- return renderToString(children);
164
- }
165
- var VOID_ELEMENTS2 = new Set([
166
- "area",
167
- "base",
168
- "br",
169
- "col",
170
- "embed",
171
- "hr",
172
- "img",
173
- "input",
174
- "link",
175
- "meta",
176
- "param",
177
- "source",
178
- "track",
179
- "wbr"
180
- ]);
181
- async function renderIntrinsicElement(tag, props, children) {
182
- const attrs = renderAttributes(props);
183
- if (VOID_ELEMENTS2.has(tag)) {
184
- return attrs ? `<${tag}${attrs}>` : `<${tag}>`;
185
- }
186
- const childrenHtml = await renderChildren(children, props);
187
- return attrs ? `<${tag}${attrs}>${childrenHtml}</${tag}>` : `<${tag}>${childrenHtml}</${tag}>`;
188
- }
189
- function renderAttributes(props) {
190
- let attrs = "";
191
- for (const [key, value] of Object.entries(props)) {
192
- if (key === "children")
193
- continue;
194
- if (value == null || value === false)
195
- continue;
196
- if (value === true) {
197
- attrs += ` ${key}`;
198
- continue;
199
- }
200
- if (key === "dangerouslySetInnerHTML") {
201
- continue;
202
- }
203
- const attrName = key === "className" ? "class" : key;
204
- const finalName = attrName === "htmlFor" ? "for" : attrName;
205
- const attrValue = escapeHtml(String(value));
206
- attrs += ` ${finalName}="${attrValue}"`;
207
- }
208
- return attrs;
209
- }
210
- async function renderChildren(children, props) {
211
- const dangerous = props.dangerouslySetInnerHTML;
212
- if (dangerous && typeof dangerous.__html === "string") {
213
- return dangerous.__html;
214
- }
215
- return renderToString(children);
216
- }
217
- function isSlots(children) {
218
- return children != null && typeof children === "object" && !Array.isArray(children) && !(children instanceof HtmlEscapedString);
219
- }
220
- // src/server/stream.ts
221
- var VOID_ELEMENTS3 = new Set([
222
- "area",
223
- "base",
224
- "br",
225
- "col",
226
- "embed",
227
- "hr",
228
- "img",
229
- "input",
230
- "link",
231
- "meta",
232
- "param",
233
- "source",
234
- "track",
235
- "wbr"
236
- ]);
237
- function streamPage(page, data) {
238
- const encoder = new TextEncoder;
239
- return new ReadableStream({
240
- async start(controller) {
241
- try {
242
- const element = await page(data);
243
- await streamNode(element, controller, encoder);
244
- controller.close();
245
- } catch (error) {
246
- controller.error(error);
247
- }
248
- }
249
- });
250
- }
251
- async function streamNode(node, controller, encoder) {
252
- if (node == null || typeof node === "boolean") {
253
- return;
254
- }
255
- if (typeof node === "string") {
256
- controller.enqueue(encoder.encode(escapeHtml(node)));
257
- return;
258
- }
259
- if (typeof node === "number") {
260
- controller.enqueue(encoder.encode(String(node)));
261
- return;
262
- }
263
- if (node instanceof HtmlEscapedString) {
264
- controller.enqueue(encoder.encode(node.value));
265
- return;
266
- }
267
- if (Array.isArray(node)) {
268
- for (const child of node) {
269
- await streamNode(child, controller, encoder);
270
- }
271
- return;
272
- }
273
- if (node instanceof Promise) {
274
- const resolved = await node;
275
- await streamElement(resolved, controller, encoder);
276
- return;
277
- }
278
- await streamElement(node, controller, encoder);
279
- }
280
- async function streamElement(element, controller, encoder) {
281
- const { tag, props, children } = element;
282
- if (typeof tag === "function") {
283
- const result = await tag(props);
284
- await streamNode(result, controller, encoder);
285
- return;
286
- }
287
- if (typeof tag === "string") {
288
- await streamIntrinsicElement(tag, props, children, controller, encoder);
289
- return;
290
- }
291
- await streamNode(children, controller, encoder);
292
- }
293
- async function streamIntrinsicElement(tag, props, children, controller, encoder) {
294
- const attrs = renderAttributes2(props);
295
- const dangerous = props.dangerouslySetInnerHTML;
296
- if (VOID_ELEMENTS3.has(tag)) {
297
- const html = attrs ? `<${tag}${attrs}>` : `<${tag}>`;
298
- controller.enqueue(encoder.encode(html));
299
- return;
300
- }
301
- const openTag = attrs ? `<${tag}${attrs}>` : `<${tag}>`;
302
- controller.enqueue(encoder.encode(openTag));
303
- if (dangerous && typeof dangerous.__html === "string") {
304
- controller.enqueue(encoder.encode(dangerous.__html));
305
- } else {
306
- await streamNode(children, controller, encoder);
307
- }
308
- controller.enqueue(encoder.encode(`</${tag}>`));
309
- }
310
- function renderAttributes2(props) {
311
- let attrs = "";
312
- for (const [key, value] of Object.entries(props)) {
313
- if (key === "children" || key === "dangerouslySetInnerHTML")
314
- continue;
315
- if (value == null || value === false)
316
- continue;
317
- if (value === true) {
318
- attrs += ` ${key}`;
319
- continue;
320
- }
321
- const attrName = key === "className" ? "class" : key === "htmlFor" ? "for" : key;
322
- const attrValue = escapeHtml(String(value));
323
- attrs += ` ${attrName}="${attrValue}"`;
324
- }
325
- return attrs;
326
- }
327
102
  // src/reactivity/scheduler.ts
328
103
  var pendingEffects = new Set;
329
104
  var flushScheduled = false;
@@ -566,79 +341,68 @@ function isComputed(value) {
566
341
  return value != null && typeof value === "object" && COMPUTED_BRAND in value;
567
342
  }
568
343
 
569
- // src/hydration/markers.ts
570
- var COMP_ID_ATTR = "data-sinwan-id";
571
- var COMP_ID_PREFIX = "c";
572
- var TEXT_MARKER_OPEN = "sinwan-t:";
573
- var TEXT_MARKER_CLOSE = "/sinwan-t";
574
- var EVENT_ATTR = "data-sinwan-ev";
575
- function compId(index) {
576
- return `${COMP_ID_PREFIX}${index}`;
577
- }
578
- function textMarkerOpen(index) {
579
- return `<!--${TEXT_MARKER_OPEN}${index}-->`;
344
+ // src/renderer/dom-ops.ts
345
+ function createDefaultDOMOps() {
346
+ return {
347
+ createElement(tag) {
348
+ return document.createElement(tag);
349
+ },
350
+ createElementNS(namespace, tag) {
351
+ return document.createElementNS(namespace, tag);
352
+ },
353
+ createTextNode(text) {
354
+ return document.createTextNode(text);
355
+ },
356
+ createComment(text) {
357
+ return document.createComment(text);
358
+ },
359
+ setAttribute(el, key, value) {
360
+ el.setAttribute(key, value);
361
+ },
362
+ removeAttribute(el, key) {
363
+ el.removeAttribute(key);
364
+ },
365
+ setProperty(el, key, value) {
366
+ el[key] = value;
367
+ },
368
+ insertBefore(parent, child, anchor) {
369
+ parent.insertBefore(child, anchor);
370
+ },
371
+ appendChild(parent, child) {
372
+ parent.appendChild(child);
373
+ },
374
+ remove(node) {
375
+ node.parentNode?.removeChild(node);
376
+ },
377
+ setTextContent(node, text) {
378
+ node.data = text;
379
+ },
380
+ addEventListener(el, event, handler) {
381
+ el.addEventListener(event, handler);
382
+ },
383
+ removeEventListener(el, event, handler) {
384
+ el.removeEventListener(event, handler);
385
+ },
386
+ parentNode(node) {
387
+ return node.parentNode;
388
+ },
389
+ nextSibling(node) {
390
+ return node.nextSibling;
391
+ }
392
+ };
580
393
  }
581
- function textMarkerCloseStr() {
582
- return `<!--${TEXT_MARKER_CLOSE}-->`;
394
+ var defaultDOMOps = createDefaultDOMOps();
395
+ var domOps = { ...defaultDOMOps };
396
+ function setDOMOps(overrides) {
397
+ Object.assign(domOps, overrides);
583
398
  }
584
- function parseTextOpenMarker(node) {
585
- const data = node.data;
586
- if (data.startsWith(TEXT_MARKER_OPEN)) {
587
- const idx = parseInt(data.slice(TEXT_MARKER_OPEN.length), 10);
588
- return Number.isNaN(idx) ? -1 : idx;
399
+ function resetDOMOps() {
400
+ for (const key of Object.keys(domOps)) {
401
+ delete domOps[key];
589
402
  }
590
- return -1;
591
- }
592
- function isTextCloseMarker(node) {
593
- return node.data === TEXT_MARKER_CLOSE;
403
+ Object.assign(domOps, defaultDOMOps);
594
404
  }
595
405
 
596
- // src/renderer/dom-ops.ts
597
- var domOps = {
598
- createElement(tag) {
599
- return document.createElement(tag);
600
- },
601
- createTextNode(text) {
602
- return document.createTextNode(text);
603
- },
604
- createComment(text) {
605
- return document.createComment(text);
606
- },
607
- setAttribute(el, key, value) {
608
- el.setAttribute(key, value);
609
- },
610
- removeAttribute(el, key) {
611
- el.removeAttribute(key);
612
- },
613
- setProperty(el, key, value) {
614
- el[key] = value;
615
- },
616
- insertBefore(parent, child, anchor) {
617
- parent.insertBefore(child, anchor);
618
- },
619
- appendChild(parent, child) {
620
- parent.appendChild(child);
621
- },
622
- remove(node) {
623
- node.parentNode?.removeChild(node);
624
- },
625
- setTextContent(node, text) {
626
- node.data = text;
627
- },
628
- addEventListener(el, event, handler) {
629
- el.addEventListener(event, handler);
630
- },
631
- removeEventListener(el, event, handler) {
632
- el.removeEventListener(event, handler);
633
- },
634
- parentNode(node) {
635
- return node.parentNode;
636
- },
637
- nextSibling(node) {
638
- return node.nextSibling;
639
- }
640
- };
641
-
642
406
  // src/renderer/events.ts
643
407
  function isEventProp(key) {
644
408
  return key.length > 2 && key[0] === "o" && key[1] === "n" && key[2] >= "A" && key[2] <= "Z";
@@ -666,24 +430,184 @@ function bindEvents(el, props) {
666
430
  return cleanups;
667
431
  }
668
432
 
669
- // src/component/instance.ts
670
- var uidCounter = 0;
671
- function createComponentInstance(component, props, parent) {
433
+ // src/component/control-flow.ts
434
+ var SHOW_TYPE = Symbol.for("Sinwan.Show");
435
+ var FOR_TYPE = Symbol.for("Sinwan.For");
436
+ function Show(props) {
672
437
  return {
673
- uid: uidCounter++,
674
- component,
438
+ tag: SHOW_TYPE,
675
439
  props,
676
- element: null,
677
- parent,
678
- children: [],
679
- effects: [],
680
- _mountedHooks: [],
681
- _unmountedHooks: [],
682
- _updatedHooks: [],
683
- _errorHooks: [],
684
- provides: parent ? Object.create(parent.provides) : Object.create(null),
685
- isMounted: false,
686
- isUnmounted: false
440
+ children: []
441
+ };
442
+ }
443
+ function For(props) {
444
+ return {
445
+ tag: FOR_TYPE,
446
+ props,
447
+ children: []
448
+ };
449
+ }
450
+ function isShowElement(element) {
451
+ return element.tag === SHOW_TYPE;
452
+ }
453
+ function isForElement(element) {
454
+ return element.tag === FOR_TYPE;
455
+ }
456
+
457
+ // src/server/renderer.ts
458
+ var componentCache = new WeakMap;
459
+ var pageRegistry = new Map;
460
+ function registerPage(name, page) {
461
+ pageRegistry.set(name, page);
462
+ }
463
+ function getPage(name) {
464
+ return pageRegistry.get(name);
465
+ }
466
+ function hasPage(name) {
467
+ return pageRegistry.has(name);
468
+ }
469
+ async function renderPage(name, data) {
470
+ const page = getPage(name);
471
+ if (!page) {
472
+ throw new Error(`Page "${name}" not found in registry`);
473
+ }
474
+ const element = await page(data);
475
+ return renderToString(element);
476
+ }
477
+ async function renderToString(node) {
478
+ if (node == null || typeof node === "boolean") {
479
+ return "";
480
+ }
481
+ if (typeof node === "string") {
482
+ return escapeHtml(node);
483
+ }
484
+ if (typeof node === "number") {
485
+ return String(node);
486
+ }
487
+ if (node instanceof HtmlEscapedString) {
488
+ return node.value;
489
+ }
490
+ if (isSignal(node) || isComputed(node)) {
491
+ return escapeHtml(String(node.value));
492
+ }
493
+ if (Array.isArray(node)) {
494
+ const results = await Promise.all(node.map((child) => renderToString(child)));
495
+ return results.join("");
496
+ }
497
+ if (node instanceof Promise) {
498
+ return renderElement(await node);
499
+ }
500
+ return renderElement(node);
501
+ }
502
+ async function renderElement(element) {
503
+ const { tag, props, children } = element;
504
+ if (isShowElement(element)) {
505
+ const when = readReactive(props.when);
506
+ return renderToString(when ? resolveShowChildren(element, when) : props.fallback);
507
+ }
508
+ if (isForElement(element)) {
509
+ return renderForElement(element);
510
+ }
511
+ if (typeof tag === "function") {
512
+ const result = await tag(props);
513
+ return renderToString(result);
514
+ }
515
+ if (typeof tag === "string") {
516
+ return renderIntrinsicElement(tag, props, children);
517
+ }
518
+ return renderToString(children);
519
+ }
520
+ var VOID_ELEMENTS2 = new Set([
521
+ "area",
522
+ "base",
523
+ "br",
524
+ "col",
525
+ "embed",
526
+ "hr",
527
+ "img",
528
+ "input",
529
+ "link",
530
+ "meta",
531
+ "param",
532
+ "source",
533
+ "track",
534
+ "wbr"
535
+ ]);
536
+ async function renderIntrinsicElement(tag, props, children) {
537
+ const attrs = renderAttributes(props);
538
+ if (VOID_ELEMENTS2.has(tag)) {
539
+ return attrs ? `<${tag}${attrs}>` : `<${tag}>`;
540
+ }
541
+ const childrenHtml = await renderChildren(children, props);
542
+ return attrs ? `<${tag}${attrs}>${childrenHtml}</${tag}>` : `<${tag}>${childrenHtml}</${tag}>`;
543
+ }
544
+ function renderAttributes(props) {
545
+ let attrs = "";
546
+ for (const [key, value] of Object.entries(props)) {
547
+ if (key === "children" || key === "key" || key === "ref" || key === "dangerouslySetInnerHTML" || isEventProp(key)) {
548
+ continue;
549
+ }
550
+ const resolvedValue = readReactive(value);
551
+ if (resolvedValue == null || resolvedValue === false)
552
+ continue;
553
+ const attrName = key === "className" ? "class" : key;
554
+ const finalName = attrName === "htmlFor" ? "for" : attrName;
555
+ if (resolvedValue === true) {
556
+ attrs += ` ${finalName}`;
557
+ continue;
558
+ }
559
+ const attrValue = escapeHtml(String(resolvedValue));
560
+ attrs += ` ${finalName}="${attrValue}"`;
561
+ }
562
+ return attrs;
563
+ }
564
+ async function renderChildren(children, props) {
565
+ const dangerous = props.dangerouslySetInnerHTML;
566
+ if (dangerous && typeof dangerous.__html === "string") {
567
+ return dangerous.__html;
568
+ }
569
+ return renderToString(children);
570
+ }
571
+ function isSlots(children) {
572
+ return children != null && typeof children === "object" && !Array.isArray(children) && !(children instanceof HtmlEscapedString);
573
+ }
574
+ async function renderForElement(element) {
575
+ const props = element.props;
576
+ const each = readReactive(props.each);
577
+ if (!Array.isArray(each) || typeof props.children !== "function") {
578
+ return "";
579
+ }
580
+ const rendered = await Promise.all(each.map((item, index) => renderToString(props.children(item, () => index))));
581
+ return rendered.join("");
582
+ }
583
+ function resolveShowChildren(element, value) {
584
+ const children = element.props.children ?? element.children;
585
+ if (typeof children === "function") {
586
+ return children(value);
587
+ }
588
+ return children;
589
+ }
590
+ function readReactive(value) {
591
+ return isSignal(value) || isComputed(value) ? value.value : value;
592
+ }
593
+ // src/component/instance.ts
594
+ var uidCounter = 0;
595
+ function createComponentInstance(component, props, parent) {
596
+ return {
597
+ uid: uidCounter++,
598
+ component,
599
+ props,
600
+ element: null,
601
+ parent,
602
+ children: [],
603
+ effects: [],
604
+ _mountedHooks: [],
605
+ _unmountedHooks: [],
606
+ _updatedHooks: [],
607
+ _errorHooks: [],
608
+ provides: parent ? Object.create(parent.provides) : Object.create(null),
609
+ isMounted: false,
610
+ isUnmounted: false
687
611
  };
688
612
  }
689
613
  var currentInstance = null;
@@ -695,7 +619,18 @@ function setCurrentInstance(instance) {
695
619
  currentInstance = instance;
696
620
  return prev;
697
621
  }
622
+ function withInstance(instance, fn) {
623
+ const prev = setCurrentInstance(instance);
624
+ try {
625
+ return fn();
626
+ } finally {
627
+ setCurrentInstance(prev);
628
+ }
629
+ }
698
630
  function fireMountedHooks(instance) {
631
+ if (instance.isUnmounted) {
632
+ return;
633
+ }
699
634
  for (const child of instance.children) {
700
635
  fireMountedHooks(child);
701
636
  }
@@ -722,6 +657,24 @@ function fireUnmountedHooks(instance) {
722
657
  instance.effects.length = 0;
723
658
  }
724
659
  }
660
+ function fireUpdatedHooks(instance) {
661
+ for (const hook of instance._updatedHooks) {
662
+ hook();
663
+ }
664
+ }
665
+ var queuedUpdatedHooks = new Set;
666
+ function queueUpdatedHooks(instance) {
667
+ if (!instance || !instance.isMounted || instance.isUnmounted || instance._updatedHooks.length === 0 || queuedUpdatedHooks.has(instance)) {
668
+ return;
669
+ }
670
+ queuedUpdatedHooks.add(instance);
671
+ nextTick(() => {
672
+ queuedUpdatedHooks.delete(instance);
673
+ if (instance.isMounted && !instance.isUnmounted) {
674
+ fireUpdatedHooks(instance);
675
+ }
676
+ });
677
+ }
725
678
  function handleComponentError(instance, err) {
726
679
  let current = instance;
727
680
  while (current) {
@@ -736,6 +689,342 @@ function handleComponentError(instance, err) {
736
689
  console.error("[Sinwan] Unhandled component error:", err);
737
690
  }
738
691
 
692
+ // src/hydration/markers.ts
693
+ var COMP_ID_ATTR = "data-sinwan-id";
694
+ var COMP_ID_PREFIX = "c";
695
+ var TEXT_MARKER_OPEN = "sinwan-t:";
696
+ var TEXT_MARKER_CLOSE = "/sinwan-t";
697
+ var EVENT_ATTR = "data-sinwan-ev";
698
+ function compId(index) {
699
+ return `${COMP_ID_PREFIX}${index}`;
700
+ }
701
+ function textMarkerOpen(index) {
702
+ return `<!--${TEXT_MARKER_OPEN}${index}-->`;
703
+ }
704
+ function textMarkerCloseStr() {
705
+ return `<!--${TEXT_MARKER_CLOSE}-->`;
706
+ }
707
+ function parseTextOpenMarker(node) {
708
+ const data = node.data;
709
+ if (data.startsWith(TEXT_MARKER_OPEN)) {
710
+ const idx = parseInt(data.slice(TEXT_MARKER_OPEN.length), 10);
711
+ return Number.isNaN(idx) ? -1 : idx;
712
+ }
713
+ return -1;
714
+ }
715
+ function isTextCloseMarker(node) {
716
+ return node.data === TEXT_MARKER_CLOSE;
717
+ }
718
+
719
+ // src/server/stream.ts
720
+ function createHydratableStreamContext() {
721
+ return { componentIndex: 0, textIndex: 0, eventIndex: 0 };
722
+ }
723
+ var VOID_ELEMENTS3 = new Set([
724
+ "area",
725
+ "base",
726
+ "br",
727
+ "col",
728
+ "embed",
729
+ "hr",
730
+ "img",
731
+ "input",
732
+ "link",
733
+ "meta",
734
+ "param",
735
+ "source",
736
+ "track",
737
+ "wbr"
738
+ ]);
739
+ function streamPage(page, data) {
740
+ const encoder = new TextEncoder;
741
+ return new ReadableStream({
742
+ async start(controller) {
743
+ try {
744
+ const element = await page(data);
745
+ await streamNode(element, controller, encoder);
746
+ controller.close();
747
+ } catch (error) {
748
+ controller.error(error);
749
+ }
750
+ }
751
+ });
752
+ }
753
+ function streamHydratablePage(component, props) {
754
+ const encoder = new TextEncoder;
755
+ const ctx = createHydratableStreamContext();
756
+ return new ReadableStream({
757
+ async start(controller) {
758
+ try {
759
+ await streamHydratableComponent(component, props ?? {}, controller, encoder, ctx, true);
760
+ controller.close();
761
+ } catch (error) {
762
+ controller.error(error);
763
+ }
764
+ }
765
+ });
766
+ }
767
+ function streamHydratableNode(node) {
768
+ const encoder = new TextEncoder;
769
+ const ctx = createHydratableStreamContext();
770
+ return new ReadableStream({
771
+ async start(controller) {
772
+ try {
773
+ await streamHydratableNodeToController(node, controller, encoder, ctx);
774
+ controller.close();
775
+ } catch (error) {
776
+ controller.error(error);
777
+ }
778
+ }
779
+ });
780
+ }
781
+ async function streamNode(node, controller, encoder) {
782
+ if (node == null || typeof node === "boolean") {
783
+ return;
784
+ }
785
+ if (typeof node === "string") {
786
+ controller.enqueue(encoder.encode(escapeHtml(node)));
787
+ return;
788
+ }
789
+ if (typeof node === "number") {
790
+ controller.enqueue(encoder.encode(String(node)));
791
+ return;
792
+ }
793
+ if (node instanceof HtmlEscapedString) {
794
+ controller.enqueue(encoder.encode(node.value));
795
+ return;
796
+ }
797
+ if (isSignal(node) || isComputed(node)) {
798
+ controller.enqueue(encoder.encode(escapeHtml(String(node.value))));
799
+ return;
800
+ }
801
+ if (Array.isArray(node)) {
802
+ for (const child of node) {
803
+ await streamNode(child, controller, encoder);
804
+ }
805
+ return;
806
+ }
807
+ if (node instanceof Promise) {
808
+ const resolved = await node;
809
+ await streamElement(resolved, controller, encoder);
810
+ return;
811
+ }
812
+ await streamElement(node, controller, encoder);
813
+ }
814
+ async function streamElement(element, controller, encoder) {
815
+ const { tag, props, children } = element;
816
+ if (isShowElement(element)) {
817
+ const when = readReactive2(props.when);
818
+ await streamNode(when ? resolveShowChildren2(element, when) : props.fallback, controller, encoder);
819
+ return;
820
+ }
821
+ if (isForElement(element)) {
822
+ await streamForElement(element, controller, encoder);
823
+ return;
824
+ }
825
+ if (typeof tag === "function") {
826
+ const result = await tag(props);
827
+ await streamNode(result, controller, encoder);
828
+ return;
829
+ }
830
+ if (typeof tag === "string") {
831
+ await streamIntrinsicElement(tag, props, children, controller, encoder);
832
+ return;
833
+ }
834
+ await streamNode(children, controller, encoder);
835
+ }
836
+ async function streamIntrinsicElement(tag, props, children, controller, encoder) {
837
+ const attrs = renderAttributes2(props);
838
+ const dangerous = props.dangerouslySetInnerHTML;
839
+ if (VOID_ELEMENTS3.has(tag)) {
840
+ const html = attrs ? `<${tag}${attrs}>` : `<${tag}>`;
841
+ controller.enqueue(encoder.encode(html));
842
+ return;
843
+ }
844
+ const openTag = attrs ? `<${tag}${attrs}>` : `<${tag}>`;
845
+ controller.enqueue(encoder.encode(openTag));
846
+ if (dangerous && typeof dangerous.__html === "string") {
847
+ controller.enqueue(encoder.encode(dangerous.__html));
848
+ } else {
849
+ await streamNode(children, controller, encoder);
850
+ }
851
+ controller.enqueue(encoder.encode(`</${tag}>`));
852
+ }
853
+ function renderAttributes2(props) {
854
+ let attrs = "";
855
+ for (const [key, value] of Object.entries(props)) {
856
+ if (key === "children" || key === "key" || key === "ref" || key === "dangerouslySetInnerHTML" || isEventProp(key)) {
857
+ continue;
858
+ }
859
+ const resolvedValue = readReactive2(value);
860
+ if (resolvedValue == null || resolvedValue === false)
861
+ continue;
862
+ const attrName = key === "className" ? "class" : key === "htmlFor" ? "for" : key;
863
+ if (resolvedValue === true) {
864
+ attrs += ` ${attrName}`;
865
+ continue;
866
+ }
867
+ const attrValue = escapeHtml(String(resolvedValue));
868
+ attrs += ` ${attrName}="${attrValue}"`;
869
+ }
870
+ return attrs;
871
+ }
872
+ async function streamHydratableNodeToController(node, controller, encoder, ctx, isComponentRoot = false) {
873
+ if (node == null || typeof node === "boolean") {
874
+ return;
875
+ }
876
+ if (typeof node === "string") {
877
+ enqueue(controller, encoder, escapeHtml(node));
878
+ return;
879
+ }
880
+ if (typeof node === "number") {
881
+ enqueue(controller, encoder, String(node));
882
+ return;
883
+ }
884
+ if (node instanceof HtmlEscapedString) {
885
+ enqueue(controller, encoder, node.value);
886
+ return;
887
+ }
888
+ if (isSignal(node) || isComputed(node)) {
889
+ const idx = ctx.textIndex++;
890
+ enqueue(controller, encoder, `${textMarkerOpen(idx)}${escapeHtml(String(node.value))}${textMarkerCloseStr()}`);
891
+ return;
892
+ }
893
+ if (Array.isArray(node)) {
894
+ for (const child of node) {
895
+ await streamHydratableNodeToController(child, controller, encoder, ctx);
896
+ }
897
+ return;
898
+ }
899
+ if (node instanceof Promise) {
900
+ await streamHydratableElement(await node, controller, encoder, ctx, isComponentRoot);
901
+ return;
902
+ }
903
+ await streamHydratableElement(node, controller, encoder, ctx, isComponentRoot);
904
+ }
905
+ async function streamHydratableElement(element, controller, encoder, ctx, isComponentRoot) {
906
+ const { tag, props, children } = element;
907
+ if (tag === "") {
908
+ for (const child of children) {
909
+ await streamHydratableNodeToController(child, controller, encoder, ctx);
910
+ }
911
+ return;
912
+ }
913
+ if (tag === Show || tag === For) {
914
+ await streamHydratableElement(tag(props), controller, encoder, ctx, isComponentRoot);
915
+ return;
916
+ }
917
+ if (isShowElement(element)) {
918
+ const when = readReactive2(props.when);
919
+ await streamHydratableNodeToController(when ? resolveShowChildren2(element, when) : props.fallback, controller, encoder, ctx, isComponentRoot);
920
+ return;
921
+ }
922
+ if (isForElement(element)) {
923
+ await streamHydratableForElement(element, controller, encoder, ctx);
924
+ return;
925
+ }
926
+ if (typeof tag === "function") {
927
+ await streamHydratableComponent(tag, props, controller, encoder, ctx, true);
928
+ return;
929
+ }
930
+ if (typeof tag === "string") {
931
+ await streamHydratableIntrinsic(tag, props, children, controller, encoder, ctx, isComponentRoot);
932
+ return;
933
+ }
934
+ await streamHydratableNodeToController(children, controller, encoder, ctx);
935
+ }
936
+ async function streamHydratableComponent(component, props, controller, encoder, ctx, isComponentRoot) {
937
+ const parentInstance = getCurrentInstance();
938
+ const instance = createComponentInstance(component, props, parentInstance);
939
+ if (parentInstance) {
940
+ parentInstance.children.push(instance);
941
+ }
942
+ const prev = setCurrentInstance(instance);
943
+ try {
944
+ const result = await component(props);
945
+ await streamHydratableNodeToController(result, controller, encoder, ctx, isComponentRoot);
946
+ } finally {
947
+ setCurrentInstance(prev);
948
+ }
949
+ }
950
+ async function streamHydratableIntrinsic(tag, props, children, controller, encoder, ctx, isComponentRoot) {
951
+ const attrs = renderHydratableAttributes(props, ctx, isComponentRoot);
952
+ const dangerous = props.dangerouslySetInnerHTML;
953
+ enqueue(controller, encoder, attrs ? `<${tag}${attrs}>` : `<${tag}>`);
954
+ if (VOID_ELEMENTS3.has(tag)) {
955
+ return;
956
+ }
957
+ if (dangerous && typeof dangerous.__html === "string") {
958
+ enqueue(controller, encoder, dangerous.__html);
959
+ } else {
960
+ for (const child of children) {
961
+ await streamHydratableNodeToController(child, controller, encoder, ctx);
962
+ }
963
+ }
964
+ enqueue(controller, encoder, `</${tag}>`);
965
+ }
966
+ async function streamHydratableForElement(element, controller, encoder, ctx) {
967
+ const props = element.props;
968
+ const each = readReactive2(props.each);
969
+ if (!Array.isArray(each) || typeof props.children !== "function") {
970
+ return;
971
+ }
972
+ for (let index = 0;index < each.length; index++) {
973
+ await streamHydratableNodeToController(props.children(each[index], () => index), controller, encoder, ctx);
974
+ }
975
+ }
976
+ function renderHydratableAttributes(props, ctx, isComponentRoot) {
977
+ let attrs = "";
978
+ if (isComponentRoot) {
979
+ attrs += ` ${COMP_ID_ATTR}="${compId(ctx.componentIndex++)}"`;
980
+ }
981
+ const eventParts = [];
982
+ for (const [key, value] of Object.entries(props)) {
983
+ if (key === "children" || key === "key" || key === "ref" || key === "dangerouslySetInnerHTML") {
984
+ continue;
985
+ }
986
+ if (isEventProp(key)) {
987
+ eventParts.push(`${toEventName(key)}:${ctx.eventIndex++}`);
988
+ continue;
989
+ }
990
+ const resolvedValue = readReactive2(value);
991
+ if (resolvedValue == null || resolvedValue === false)
992
+ continue;
993
+ const attrName = key === "className" ? "class" : key === "htmlFor" ? "for" : key;
994
+ if (resolvedValue === true) {
995
+ attrs += ` ${attrName}`;
996
+ continue;
997
+ }
998
+ attrs += ` ${attrName}="${escapeHtml(String(resolvedValue))}"`;
999
+ }
1000
+ if (eventParts.length > 0) {
1001
+ attrs += ` ${EVENT_ATTR}="${eventParts.join(",")}"`;
1002
+ }
1003
+ return attrs;
1004
+ }
1005
+ function enqueue(controller, encoder, html) {
1006
+ controller.enqueue(encoder.encode(html));
1007
+ }
1008
+ async function streamForElement(element, controller, encoder) {
1009
+ const props = element.props;
1010
+ const each = readReactive2(props.each);
1011
+ if (!Array.isArray(each) || typeof props.children !== "function") {
1012
+ return;
1013
+ }
1014
+ for (let index = 0;index < each.length; index++) {
1015
+ await streamNode(props.children(each[index], () => index), controller, encoder);
1016
+ }
1017
+ }
1018
+ function resolveShowChildren2(element, value) {
1019
+ const children = element.props.children ?? element.children;
1020
+ if (typeof children === "function") {
1021
+ return children(value);
1022
+ }
1023
+ return children;
1024
+ }
1025
+ function readReactive2(value) {
1026
+ return isSignal(value) || isComputed(value) ? value.value : value;
1027
+ }
739
1028
  // src/server/hydration-markers.ts
740
1029
  function createHydrationContext() {
741
1030
  return { componentIndex: 0, textIndex: 0, eventIndex: 0 };
@@ -744,13 +1033,16 @@ async function renderToHydratableString(component, props) {
744
1033
  const ctx = createHydrationContext();
745
1034
  const mergedProps = props ?? {};
746
1035
  const instance = createComponentInstance(component, mergedProps, null);
747
- setCurrentInstance(instance);
748
- const result = await component(mergedProps);
749
- setCurrentInstance(null);
750
- if (result && typeof result === "object" && "tag" in result) {
751
- return renderElementH(result, ctx, true);
1036
+ const prev = setCurrentInstance(instance);
1037
+ try {
1038
+ const result = await component(mergedProps);
1039
+ if (result && typeof result === "object" && "tag" in result) {
1040
+ return renderElementH(result, ctx, true);
1041
+ }
1042
+ return renderNodeH(result, ctx);
1043
+ } finally {
1044
+ setCurrentInstance(prev);
752
1045
  }
753
- return renderNodeH(result, ctx);
754
1046
  }
755
1047
  async function renderNodeToHydratableString(node) {
756
1048
  const ctx = createHydrationContext();
@@ -802,6 +1094,17 @@ function renderElementH(element, ctx, isComponentRoot) {
802
1094
  if (tag === "") {
803
1095
  return children.map((child) => renderNodeH(child, ctx)).join("");
804
1096
  }
1097
+ if (tag === Show || tag === For) {
1098
+ return renderElementH(tag(props), ctx, isComponentRoot);
1099
+ }
1100
+ if (isShowElement(element)) {
1101
+ const when = readReactive3(props.when);
1102
+ const content = when ? resolveShowChildren3(element, when) : props.fallback;
1103
+ return renderNodeMaybeRoot(content, ctx, isComponentRoot);
1104
+ }
1105
+ if (isForElement(element)) {
1106
+ return renderForElementH(element, ctx);
1107
+ }
805
1108
  if (typeof tag === "function") {
806
1109
  return renderComponentH(tag, props, ctx);
807
1110
  }
@@ -811,15 +1114,21 @@ function renderElementH(element, ctx, isComponentRoot) {
811
1114
  return children.map((child) => renderNodeH(child, ctx)).join("");
812
1115
  }
813
1116
  function renderComponentH(component, props, ctx) {
814
- const parentInstance = globalThis.__SinwanCurrentInstance;
815
- const instance = createComponentInstance(component, props, null);
1117
+ const parentInstance = getCurrentInstance();
1118
+ const instance = createComponentInstance(component, props, parentInstance);
1119
+ if (parentInstance) {
1120
+ parentInstance.children.push(instance);
1121
+ }
816
1122
  const prev = setCurrentInstance(instance);
817
- const result = component(props);
818
- setCurrentInstance(prev);
819
- if (result && typeof result === "object" && "tag" in result) {
820
- return renderElementH(result, ctx, true);
1123
+ try {
1124
+ const result = component(props);
1125
+ if (result && typeof result === "object" && "tag" in result) {
1126
+ return renderElementH(result, ctx, true);
1127
+ }
1128
+ return renderNodeH(result, ctx);
1129
+ } finally {
1130
+ setCurrentInstance(prev);
821
1131
  }
822
- return renderNodeH(result, ctx);
823
1132
  }
824
1133
  function renderIntrinsicH(tag, props, children, ctx, isComponentRoot) {
825
1134
  let attrs = "";
@@ -828,8 +1137,9 @@ function renderIntrinsicH(tag, props, children, ctx, isComponentRoot) {
828
1137
  }
829
1138
  const eventParts = [];
830
1139
  for (const [key, value] of Object.entries(props)) {
831
- if (key === "children" || key === "dangerouslySetInnerHTML")
1140
+ if (key === "children" || key === "key" || key === "ref" || key === "dangerouslySetInnerHTML") {
832
1141
  continue;
1142
+ }
833
1143
  if (isEventProp(key)) {
834
1144
  const eventName = toEventName(key);
835
1145
  eventParts.push(`${eventName}:${ctx.eventIndex++}`);
@@ -862,8 +1172,34 @@ function renderIntrinsicH(tag, props, children, ctx, isComponentRoot) {
862
1172
  const childrenHtml = children.map((child) => renderNodeH(child, ctx)).join("");
863
1173
  return `<${tag}${attrs}>${childrenHtml}</${tag}>`;
864
1174
  }
1175
+ function renderNodeMaybeRoot(node, ctx, isComponentRoot) {
1176
+ if (isComponentRoot && node && typeof node === "object" && !Array.isArray(node) && "tag" in node) {
1177
+ return renderElementH(node, ctx, true);
1178
+ }
1179
+ return renderNodeH(node, ctx);
1180
+ }
1181
+ function renderForElementH(element, ctx) {
1182
+ const props = element.props;
1183
+ const each = readReactive3(props.each);
1184
+ if (!Array.isArray(each) || typeof props.children !== "function") {
1185
+ return "";
1186
+ }
1187
+ return each.map((item, index) => renderNodeH(props.children(item, () => index), ctx)).join("");
1188
+ }
1189
+ function resolveShowChildren3(element, value) {
1190
+ const children = element.props.children ?? element.children;
1191
+ if (typeof children === "function") {
1192
+ return children(value);
1193
+ }
1194
+ return children;
1195
+ }
1196
+ function readReactive3(value) {
1197
+ return isSignal(value) || isComputed(value) ? value.value : value;
1198
+ }
865
1199
  export {
866
1200
  streamPage,
1201
+ streamHydratablePage,
1202
+ streamHydratableNode,
867
1203
  renderToString,
868
1204
  renderToHydratableString,
869
1205
  renderPage,
@@ -874,5 +1210,5 @@ export {
874
1210
  getPage
875
1211
  };
876
1212
 
877
- //# debugId=46FB02649D98C84264756E2164756E21
1213
+ //# debugId=E47F9E2EB5705D1864756E2164756E21
878
1214
  //# sourceMappingURL=index.development.js.map