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
@@ -87,22 +87,12 @@ function buildElement(type, props, children) {
87
87
  if (type === Fragment) {
88
88
  return { tag: "", props: {}, children };
89
89
  }
90
- if (typeof type === "function") {
91
- if (type._SinwanComponent || type._SinwanPage) {
92
- const result2 = type(props);
93
- if (result2 && typeof result2 === "object" && "tag" in result2) {
94
- return result2;
95
- }
96
- return { tag: "", props: {}, children: normalizeChildren(result2) };
97
- }
98
- const result = type(props);
99
- if (result && typeof result === "object" && "tag" in result) {
100
- return result;
90
+ if (typeof type === "function" || typeof type === "string") {
91
+ const finalProps = props ?? {};
92
+ if (children.length > 0 && finalProps.children === undefined) {
93
+ finalProps.children = children.length === 1 ? children[0] : children;
101
94
  }
102
- return { tag: "", props: {}, children: normalizeChildren(result) };
103
- }
104
- if (typeof type === "string") {
105
- return { tag: type, props: props || {}, children };
95
+ return { tag: type, props: finalProps, children };
106
96
  }
107
97
  return { tag: "", props: {}, children };
108
98
  }
@@ -122,399 +112,217 @@ function jsxDEV(type, props, key, isStaticChildren, source, self) {
122
112
  return element;
123
113
  }
124
114
 
125
- // src/server/index.ts
126
- var exports_server = {};
127
- __export(exports_server, {
128
- streamPage: () => streamPage,
129
- renderToString: () => renderToString,
130
- renderToHydratableString: () => renderToHydratableString,
131
- renderPage: () => renderPage,
132
- renderNodeToHydratableString: () => renderNodeToHydratableString,
133
- registerPage: () => registerPage,
134
- isSlots: () => isSlots,
135
- hasPage: () => hasPage,
136
- getPage: () => getPage
115
+ // src/renderer/index.ts
116
+ var exports_renderer = {};
117
+ __export(exports_renderer, {
118
+ unmountNode: () => unmountNode,
119
+ toEventName: () => toEventName,
120
+ setDOMOps: () => setDOMOps,
121
+ resetDOMOps: () => resetDOMOps,
122
+ renderNodeToDOM: () => renderNodeToDOM,
123
+ renderElementToDOM: () => renderElementToDOM,
124
+ renderChildrenToDOM: () => renderChildrenToDOM,
125
+ render: () => render,
126
+ mount: () => mount,
127
+ isEventProp: () => isEventProp,
128
+ domOps: () => domOps,
129
+ bindEvents: () => bindEvents,
130
+ bindEvent: () => bindEvent,
131
+ applyAttributes: () => applyAttributes
137
132
  });
138
- module.exports = __toCommonJS(exports_server);
133
+ module.exports = __toCommonJS(exports_renderer);
139
134
 
140
- // src/escaper.ts
141
- var _bun = globalThis.Bun;
142
- var _nativeEscape = typeof _bun?.escapeHTML === "function" ? _bun.escapeHTML.bind(_bun) : undefined;
143
- var HTML_ESCAPE_RE = /[&<>"']/g;
144
- var HTML_ESCAPE_MAP = {
145
- "&": "&amp;",
146
- "<": "&lt;",
147
- ">": "&gt;",
148
- '"': "&quot;",
149
- "'": "&#39;"
150
- };
151
- function portableEscape(str) {
152
- HTML_ESCAPE_RE.lastIndex = 0;
153
- if (!HTML_ESCAPE_RE.test(str))
154
- return str;
155
- return str.replace(HTML_ESCAPE_RE, (c) => HTML_ESCAPE_MAP[c]);
156
- }
157
- function escapeHtml(value) {
158
- if (value == null || typeof value === "boolean")
159
- return "";
160
- if (typeof value === "number")
161
- return String(value);
162
- if (value instanceof HtmlEscapedString)
163
- return value.value;
164
- const s = String(value);
165
- return _nativeEscape ? _nativeEscape(s) : portableEscape(s);
135
+ // src/renderer/dom-ops.ts
136
+ function createDefaultDOMOps() {
137
+ return {
138
+ createElement(tag) {
139
+ return document.createElement(tag);
140
+ },
141
+ createElementNS(namespace, tag) {
142
+ return document.createElementNS(namespace, tag);
143
+ },
144
+ createTextNode(text) {
145
+ return document.createTextNode(text);
146
+ },
147
+ createComment(text) {
148
+ return document.createComment(text);
149
+ },
150
+ setAttribute(el, key, value) {
151
+ el.setAttribute(key, value);
152
+ },
153
+ removeAttribute(el, key) {
154
+ el.removeAttribute(key);
155
+ },
156
+ setProperty(el, key, value) {
157
+ el[key] = value;
158
+ },
159
+ insertBefore(parent, child, anchor) {
160
+ parent.insertBefore(child, anchor);
161
+ },
162
+ appendChild(parent, child) {
163
+ parent.appendChild(child);
164
+ },
165
+ remove(node) {
166
+ node.parentNode?.removeChild(node);
167
+ },
168
+ setTextContent(node, text) {
169
+ node.data = text;
170
+ },
171
+ addEventListener(el, event, handler) {
172
+ el.addEventListener(event, handler);
173
+ },
174
+ removeEventListener(el, event, handler) {
175
+ el.removeEventListener(event, handler);
176
+ },
177
+ parentNode(node) {
178
+ return node.parentNode;
179
+ },
180
+ nextSibling(node) {
181
+ return node.nextSibling;
182
+ }
183
+ };
166
184
  }
167
- function safeHtml(html) {
168
- return raw(html);
185
+ var defaultDOMOps = createDefaultDOMOps();
186
+ var domOps = { ...defaultDOMOps };
187
+ function setDOMOps(overrides) {
188
+ Object.assign(domOps, overrides);
169
189
  }
170
- function isSafeHtml(value) {
171
- return value instanceof HtmlEscapedString;
190
+ function resetDOMOps() {
191
+ for (const key of Object.keys(domOps)) {
192
+ delete domOps[key];
193
+ }
194
+ Object.assign(domOps, defaultDOMOps);
172
195
  }
173
196
 
174
- // src/server/renderer.ts
175
- var componentCache = new WeakMap;
176
- var pageRegistry = new Map;
177
- function registerPage(name, page) {
178
- pageRegistry.set(name, page);
179
- }
180
- function getPage(name) {
181
- return pageRegistry.get(name);
197
+ // src/reactivity/scheduler.ts
198
+ var pendingEffects = new Set;
199
+ var flushScheduled = false;
200
+ var isFlushing = false;
201
+ var pendingCallbacks = [];
202
+ function scheduleEffect(effect) {
203
+ if (!effect.active)
204
+ return;
205
+ pendingEffects.add(effect);
206
+ scheduleFlush();
182
207
  }
183
- function hasPage(name) {
184
- return pageRegistry.has(name);
208
+ function unscheduleEffect(effect) {
209
+ pendingEffects.delete(effect);
185
210
  }
186
- async function renderPage(name, data) {
187
- const page = getPage(name);
188
- if (!page) {
189
- throw new Error(`Page "${name}" not found in registry`);
211
+ function scheduleFlush() {
212
+ if (!flushScheduled) {
213
+ flushScheduled = true;
214
+ queueMicrotask(flush);
190
215
  }
191
- const element = await page(data);
192
- return renderToString(element);
193
216
  }
194
- async function renderToString(node) {
195
- if (node == null || typeof node === "boolean") {
196
- return "";
197
- }
198
- if (typeof node === "string") {
199
- return escapeHtml(node);
200
- }
201
- if (typeof node === "number") {
202
- return String(node);
203
- }
204
- if (node instanceof HtmlEscapedString) {
205
- return node.value;
217
+ function flush() {
218
+ isFlushing = true;
219
+ const sorted = [...pendingEffects].sort((a, b) => a.id - b.id);
220
+ pendingEffects.clear();
221
+ for (const effect of sorted) {
222
+ if (effect.active) {
223
+ effect.run();
224
+ }
206
225
  }
207
- if (Array.isArray(node)) {
208
- const results = await Promise.all(node.map((child) => renderToString(child)));
209
- return results.join("");
226
+ let safety = 10;
227
+ while (pendingEffects.size > 0 && safety-- > 0) {
228
+ const next = [...pendingEffects].sort((a, b) => a.id - b.id);
229
+ pendingEffects.clear();
230
+ for (const effect of next) {
231
+ if (effect.active) {
232
+ effect.run();
233
+ }
234
+ }
210
235
  }
211
- if (node instanceof Promise) {
212
- return renderElement(await node);
236
+ flushScheduled = false;
237
+ isFlushing = false;
238
+ const cbs = pendingCallbacks.splice(0);
239
+ for (const cb of cbs) {
240
+ cb();
213
241
  }
214
- return renderElement(node);
215
242
  }
216
- async function renderElement(element) {
217
- const { tag, props, children } = element;
218
- if (typeof tag === "function") {
219
- const result = await tag(props);
220
- return renderToString(result);
221
- }
222
- if (typeof tag === "string") {
223
- return renderIntrinsicElement(tag, props, children);
224
- }
225
- return renderToString(children);
243
+ function nextTick(fn) {
244
+ return new Promise((resolve) => {
245
+ const callback = () => {
246
+ fn?.();
247
+ resolve();
248
+ };
249
+ if (flushScheduled || isFlushing) {
250
+ pendingCallbacks.push(callback);
251
+ } else {
252
+ queueMicrotask(callback);
253
+ }
254
+ });
226
255
  }
227
- var VOID_ELEMENTS2 = new Set([
228
- "area",
229
- "base",
230
- "br",
231
- "col",
232
- "embed",
233
- "hr",
234
- "img",
235
- "input",
236
- "link",
237
- "meta",
238
- "param",
239
- "source",
240
- "track",
241
- "wbr"
242
- ]);
243
- async function renderIntrinsicElement(tag, props, children) {
244
- const attrs = renderAttributes(props);
245
- if (VOID_ELEMENTS2.has(tag)) {
246
- return attrs ? `<${tag}${attrs}>` : `<${tag}>`;
256
+ function flushSync() {
257
+ if (flushScheduled) {
258
+ flushScheduled = false;
259
+ flush();
247
260
  }
248
- const childrenHtml = await renderChildren(children, props);
249
- return attrs ? `<${tag}${attrs}>${childrenHtml}</${tag}>` : `<${tag}>${childrenHtml}</${tag}>`;
250
261
  }
251
- function renderAttributes(props) {
252
- let attrs = "";
253
- for (const [key, value] of Object.entries(props)) {
254
- if (key === "children")
255
- continue;
256
- if (value == null || value === false)
257
- continue;
258
- if (value === true) {
259
- attrs += ` ${key}`;
260
- continue;
262
+
263
+ // src/reactivity/effect.ts
264
+ var activeEffect = null;
265
+ var effectStack = [];
266
+ var effectIdCounter = 0;
267
+
268
+ class ReactiveEffect {
269
+ id;
270
+ active = true;
271
+ fn;
272
+ cleanup = undefined;
273
+ deps = new Set;
274
+ constructor(fn) {
275
+ this.id = effectIdCounter++;
276
+ this.fn = fn;
277
+ }
278
+ run() {
279
+ if (!this.active)
280
+ return;
281
+ if (effectStack.includes(this))
282
+ return;
283
+ this.cleanupDeps();
284
+ if (this.cleanup) {
285
+ this.cleanup();
286
+ this.cleanup = undefined;
261
287
  }
262
- if (key === "dangerouslySetInnerHTML") {
263
- continue;
288
+ effectStack.push(this);
289
+ const prevEffect = activeEffect;
290
+ activeEffect = this;
291
+ try {
292
+ const result = this.fn();
293
+ if (typeof result === "function") {
294
+ this.cleanup = result;
295
+ }
296
+ } finally {
297
+ activeEffect = prevEffect;
298
+ effectStack.pop();
264
299
  }
265
- const attrName = key === "className" ? "class" : key;
266
- const finalName = attrName === "htmlFor" ? "for" : attrName;
267
- const attrValue = escapeHtml(String(value));
268
- attrs += ` ${finalName}="${attrValue}"`;
269
300
  }
270
- return attrs;
271
- }
272
- async function renderChildren(children, props) {
273
- const dangerous = props.dangerouslySetInnerHTML;
274
- if (dangerous && typeof dangerous.__html === "string") {
275
- return dangerous.__html;
301
+ cleanupDeps() {
302
+ for (const dep of this.deps) {
303
+ dep.subscribers.delete(this);
304
+ }
305
+ this.deps.clear();
306
+ }
307
+ notify() {
308
+ scheduleEffect(this);
309
+ }
310
+ dispose() {
311
+ if (!this.active)
312
+ return;
313
+ this.active = false;
314
+ if (this.cleanup) {
315
+ this.cleanup();
316
+ this.cleanup = undefined;
317
+ }
318
+ this.cleanupDeps();
319
+ unscheduleEffect(this);
276
320
  }
277
- return renderToString(children);
278
321
  }
279
- function isSlots(children) {
280
- return children != null && typeof children === "object" && !Array.isArray(children) && !(children instanceof HtmlEscapedString);
281
- }
282
- // src/server/stream.ts
283
- var VOID_ELEMENTS3 = new Set([
284
- "area",
285
- "base",
286
- "br",
287
- "col",
288
- "embed",
289
- "hr",
290
- "img",
291
- "input",
292
- "link",
293
- "meta",
294
- "param",
295
- "source",
296
- "track",
297
- "wbr"
298
- ]);
299
- function streamPage(page, data) {
300
- const encoder = new TextEncoder;
301
- return new ReadableStream({
302
- async start(controller) {
303
- try {
304
- const element = await page(data);
305
- await streamNode(element, controller, encoder);
306
- controller.close();
307
- } catch (error) {
308
- controller.error(error);
309
- }
310
- }
311
- });
312
- }
313
- async function streamNode(node, controller, encoder) {
314
- if (node == null || typeof node === "boolean") {
315
- return;
316
- }
317
- if (typeof node === "string") {
318
- controller.enqueue(encoder.encode(escapeHtml(node)));
319
- return;
320
- }
321
- if (typeof node === "number") {
322
- controller.enqueue(encoder.encode(String(node)));
323
- return;
324
- }
325
- if (node instanceof HtmlEscapedString) {
326
- controller.enqueue(encoder.encode(node.value));
327
- return;
328
- }
329
- if (Array.isArray(node)) {
330
- for (const child of node) {
331
- await streamNode(child, controller, encoder);
332
- }
333
- return;
334
- }
335
- if (node instanceof Promise) {
336
- const resolved = await node;
337
- await streamElement(resolved, controller, encoder);
338
- return;
339
- }
340
- await streamElement(node, controller, encoder);
341
- }
342
- async function streamElement(element, controller, encoder) {
343
- const { tag, props, children } = element;
344
- if (typeof tag === "function") {
345
- const result = await tag(props);
346
- await streamNode(result, controller, encoder);
347
- return;
348
- }
349
- if (typeof tag === "string") {
350
- await streamIntrinsicElement(tag, props, children, controller, encoder);
351
- return;
352
- }
353
- await streamNode(children, controller, encoder);
354
- }
355
- async function streamIntrinsicElement(tag, props, children, controller, encoder) {
356
- const attrs = renderAttributes2(props);
357
- const dangerous = props.dangerouslySetInnerHTML;
358
- if (VOID_ELEMENTS3.has(tag)) {
359
- const html = attrs ? `<${tag}${attrs}>` : `<${tag}>`;
360
- controller.enqueue(encoder.encode(html));
361
- return;
362
- }
363
- const openTag = attrs ? `<${tag}${attrs}>` : `<${tag}>`;
364
- controller.enqueue(encoder.encode(openTag));
365
- if (dangerous && typeof dangerous.__html === "string") {
366
- controller.enqueue(encoder.encode(dangerous.__html));
367
- } else {
368
- await streamNode(children, controller, encoder);
369
- }
370
- controller.enqueue(encoder.encode(`</${tag}>`));
371
- }
372
- function renderAttributes2(props) {
373
- let attrs = "";
374
- for (const [key, value] of Object.entries(props)) {
375
- if (key === "children" || key === "dangerouslySetInnerHTML")
376
- continue;
377
- if (value == null || value === false)
378
- continue;
379
- if (value === true) {
380
- attrs += ` ${key}`;
381
- continue;
382
- }
383
- const attrName = key === "className" ? "class" : key === "htmlFor" ? "for" : key;
384
- const attrValue = escapeHtml(String(value));
385
- attrs += ` ${attrName}="${attrValue}"`;
386
- }
387
- return attrs;
388
- }
389
- // src/reactivity/scheduler.ts
390
- var pendingEffects = new Set;
391
- var flushScheduled = false;
392
- var isFlushing = false;
393
- var pendingCallbacks = [];
394
- function scheduleEffect(effect) {
395
- if (!effect.active)
396
- return;
397
- pendingEffects.add(effect);
398
- scheduleFlush();
399
- }
400
- function unscheduleEffect(effect) {
401
- pendingEffects.delete(effect);
402
- }
403
- function scheduleFlush() {
404
- if (!flushScheduled) {
405
- flushScheduled = true;
406
- queueMicrotask(flush);
407
- }
408
- }
409
- function flush() {
410
- isFlushing = true;
411
- const sorted = [...pendingEffects].sort((a, b) => a.id - b.id);
412
- pendingEffects.clear();
413
- for (const effect of sorted) {
414
- if (effect.active) {
415
- effect.run();
416
- }
417
- }
418
- let safety = 10;
419
- while (pendingEffects.size > 0 && safety-- > 0) {
420
- const next = [...pendingEffects].sort((a, b) => a.id - b.id);
421
- pendingEffects.clear();
422
- for (const effect of next) {
423
- if (effect.active) {
424
- effect.run();
425
- }
426
- }
427
- }
428
- flushScheduled = false;
429
- isFlushing = false;
430
- const cbs = pendingCallbacks.splice(0);
431
- for (const cb of cbs) {
432
- cb();
433
- }
434
- }
435
- function nextTick(fn) {
436
- return new Promise((resolve) => {
437
- const callback = () => {
438
- fn?.();
439
- resolve();
440
- };
441
- if (flushScheduled || isFlushing) {
442
- pendingCallbacks.push(callback);
443
- } else {
444
- queueMicrotask(callback);
445
- }
446
- });
447
- }
448
- function flushSync() {
449
- if (flushScheduled) {
450
- flushScheduled = false;
451
- flush();
452
- }
453
- }
454
-
455
- // src/reactivity/effect.ts
456
- var activeEffect = null;
457
- var effectStack = [];
458
- var effectIdCounter = 0;
459
-
460
- class ReactiveEffect {
461
- id;
462
- active = true;
463
- fn;
464
- cleanup = undefined;
465
- deps = new Set;
466
- constructor(fn) {
467
- this.id = effectIdCounter++;
468
- this.fn = fn;
469
- }
470
- run() {
471
- if (!this.active)
472
- return;
473
- if (effectStack.includes(this))
474
- return;
475
- this.cleanupDeps();
476
- if (this.cleanup) {
477
- this.cleanup();
478
- this.cleanup = undefined;
479
- }
480
- effectStack.push(this);
481
- const prevEffect = activeEffect;
482
- activeEffect = this;
483
- try {
484
- const result = this.fn();
485
- if (typeof result === "function") {
486
- this.cleanup = result;
487
- }
488
- } finally {
489
- activeEffect = prevEffect;
490
- effectStack.pop();
491
- }
492
- }
493
- cleanupDeps() {
494
- for (const dep of this.deps) {
495
- dep.subscribers.delete(this);
496
- }
497
- this.deps.clear();
498
- }
499
- notify() {
500
- scheduleEffect(this);
501
- }
502
- dispose() {
503
- if (!this.active)
504
- return;
505
- this.active = false;
506
- if (this.cleanup) {
507
- this.cleanup();
508
- this.cleanup = undefined;
509
- }
510
- this.cleanupDeps();
511
- unscheduleEffect(this);
512
- }
513
- }
514
- function effect(fn) {
515
- const e = new ReactiveEffect(fn);
516
- e.run();
517
- return () => e.dispose();
322
+ function effect(fn) {
323
+ const e = new ReactiveEffect(fn);
324
+ e.run();
325
+ return () => e.dispose();
518
326
  }
519
327
  function track(dep) {
520
328
  if (activeEffect) {
@@ -628,91 +436,18 @@ function isComputed(value) {
628
436
  return value != null && typeof value === "object" && COMPUTED_BRAND in value;
629
437
  }
630
438
 
631
- // src/hydration/markers.ts
632
- var COMP_ID_ATTR = "data-sinwan-id";
633
- var COMP_ID_PREFIX = "c";
634
- var TEXT_MARKER_OPEN = "sinwan-t:";
635
- var TEXT_MARKER_CLOSE = "/sinwan-t";
636
- var EVENT_ATTR = "data-sinwan-ev";
637
- function compId(index) {
638
- return `${COMP_ID_PREFIX}${index}`;
439
+ // src/renderer/events.ts
440
+ function isEventProp(key) {
441
+ return key.length > 2 && key[0] === "o" && key[1] === "n" && key[2] >= "A" && key[2] <= "Z";
639
442
  }
640
- function textMarkerOpen(index) {
641
- return `<!--${TEXT_MARKER_OPEN}${index}-->`;
443
+ function toEventName(key) {
444
+ return key.slice(2).toLowerCase();
642
445
  }
643
- function textMarkerCloseStr() {
644
- return `<!--${TEXT_MARKER_CLOSE}-->`;
645
- }
646
- function parseTextOpenMarker(node) {
647
- const data = node.data;
648
- if (data.startsWith(TEXT_MARKER_OPEN)) {
649
- const idx = parseInt(data.slice(TEXT_MARKER_OPEN.length), 10);
650
- return Number.isNaN(idx) ? -1 : idx;
651
- }
652
- return -1;
653
- }
654
- function isTextCloseMarker(node) {
655
- return node.data === TEXT_MARKER_CLOSE;
656
- }
657
-
658
- // src/renderer/dom-ops.ts
659
- var domOps = {
660
- createElement(tag) {
661
- return document.createElement(tag);
662
- },
663
- createTextNode(text) {
664
- return document.createTextNode(text);
665
- },
666
- createComment(text) {
667
- return document.createComment(text);
668
- },
669
- setAttribute(el, key, value) {
670
- el.setAttribute(key, value);
671
- },
672
- removeAttribute(el, key) {
673
- el.removeAttribute(key);
674
- },
675
- setProperty(el, key, value) {
676
- el[key] = value;
677
- },
678
- insertBefore(parent, child, anchor) {
679
- parent.insertBefore(child, anchor);
680
- },
681
- appendChild(parent, child) {
682
- parent.appendChild(child);
683
- },
684
- remove(node) {
685
- node.parentNode?.removeChild(node);
686
- },
687
- setTextContent(node, text) {
688
- node.data = text;
689
- },
690
- addEventListener(el, event, handler) {
691
- el.addEventListener(event, handler);
692
- },
693
- removeEventListener(el, event, handler) {
694
- el.removeEventListener(event, handler);
695
- },
696
- parentNode(node) {
697
- return node.parentNode;
698
- },
699
- nextSibling(node) {
700
- return node.nextSibling;
701
- }
702
- };
703
-
704
- // src/renderer/events.ts
705
- function isEventProp(key) {
706
- return key.length > 2 && key[0] === "o" && key[1] === "n" && key[2] >= "A" && key[2] <= "Z";
707
- }
708
- function toEventName(key) {
709
- return key.slice(2).toLowerCase();
710
- }
711
- function bindEvent(el, eventName, handler) {
712
- domOps.addEventListener(el, eventName, handler);
713
- return () => {
714
- domOps.removeEventListener(el, eventName, handler);
715
- };
446
+ function bindEvent(el, eventName, handler) {
447
+ domOps.addEventListener(el, eventName, handler);
448
+ return () => {
449
+ domOps.removeEventListener(el, eventName, handler);
450
+ };
716
451
  }
717
452
  function bindEvents(el, props) {
718
453
  const cleanups = [];
@@ -757,7 +492,18 @@ function setCurrentInstance(instance) {
757
492
  currentInstance = instance;
758
493
  return prev;
759
494
  }
495
+ function withInstance(instance, fn) {
496
+ const prev = setCurrentInstance(instance);
497
+ try {
498
+ return fn();
499
+ } finally {
500
+ setCurrentInstance(prev);
501
+ }
502
+ }
760
503
  function fireMountedHooks(instance) {
504
+ if (instance.isUnmounted) {
505
+ return;
506
+ }
761
507
  for (const child of instance.children) {
762
508
  fireMountedHooks(child);
763
509
  }
@@ -784,6 +530,24 @@ function fireUnmountedHooks(instance) {
784
530
  instance.effects.length = 0;
785
531
  }
786
532
  }
533
+ function fireUpdatedHooks(instance) {
534
+ for (const hook of instance._updatedHooks) {
535
+ hook();
536
+ }
537
+ }
538
+ var queuedUpdatedHooks = new Set;
539
+ function queueUpdatedHooks(instance) {
540
+ if (!instance || !instance.isMounted || instance.isUnmounted || instance._updatedHooks.length === 0 || queuedUpdatedHooks.has(instance)) {
541
+ return;
542
+ }
543
+ queuedUpdatedHooks.add(instance);
544
+ nextTick(() => {
545
+ queuedUpdatedHooks.delete(instance);
546
+ if (instance.isMounted && !instance.isUnmounted) {
547
+ fireUpdatedHooks(instance);
548
+ }
549
+ });
550
+ }
787
551
  function handleComponentError(instance, err) {
788
552
  let current = instance;
789
553
  while (current) {
@@ -798,27 +562,327 @@ function handleComponentError(instance, err) {
798
562
  console.error("[Sinwan] Unhandled component error:", err);
799
563
  }
800
564
 
801
- // src/server/hydration-markers.ts
802
- function createHydrationContext() {
803
- return { componentIndex: 0, textIndex: 0, eventIndex: 0 };
565
+ // src/renderer/attributes.ts
566
+ var SKIP_PROPS = new Set(["children", "key", "ref", "dangerouslySetInnerHTML"]);
567
+ var DOM_PROPERTIES = new Set(["value", "checked", "selected", "disabled", "readOnly", "multiple", "indeterminate"]);
568
+ var PROP_ALIASES = {
569
+ className: "class",
570
+ htmlFor: "for",
571
+ tabIndex: "tabindex",
572
+ crossOrigin: "crossorigin"
573
+ };
574
+ function applyAttributes(el, props) {
575
+ const disposers = [];
576
+ const owner = getCurrentInstance();
577
+ for (const [key, value] of Object.entries(props)) {
578
+ if (SKIP_PROPS.has(key) || isEventProp(key))
579
+ continue;
580
+ if (isSignal(value) || isComputed(value)) {
581
+ let initialized = false;
582
+ const dispose = effect(() => {
583
+ setSingleAttribute(el, key, value.value);
584
+ if (initialized) {
585
+ queueUpdatedHooks(owner);
586
+ }
587
+ initialized = true;
588
+ });
589
+ disposers.push(dispose);
590
+ } else {
591
+ setSingleAttribute(el, key, value);
592
+ }
593
+ }
594
+ return disposers;
804
595
  }
805
- async function renderToHydratableString(component, props) {
806
- const ctx = createHydrationContext();
807
- const mergedProps = props ?? {};
808
- const instance = createComponentInstance(component, mergedProps, null);
809
- setCurrentInstance(instance);
810
- const result = await component(mergedProps);
811
- setCurrentInstance(null);
812
- if (result && typeof result === "object" && "tag" in result) {
813
- return renderElementH(result, ctx, true);
596
+ function setSingleAttribute(el, key, value) {
597
+ const attrName = PROP_ALIASES[key] ?? key;
598
+ if (attrName === "style" && typeof value === "object" && value !== null) {
599
+ applyStyle(el, value);
600
+ return;
601
+ }
602
+ if (attrName === "class" && typeof value === "object" && value !== null) {
603
+ applyClass(el, value);
604
+ return;
605
+ }
606
+ if (value == null || value === false) {
607
+ domOps.removeAttribute(el, attrName);
608
+ if (DOM_PROPERTIES.has(attrName)) {
609
+ domOps.setProperty(el, attrName, attrName === "value" ? "" : false);
610
+ }
611
+ return;
612
+ }
613
+ if (value === true) {
614
+ domOps.setAttribute(el, attrName, "");
615
+ if (DOM_PROPERTIES.has(attrName)) {
616
+ domOps.setProperty(el, attrName, true);
617
+ }
618
+ return;
619
+ }
620
+ if (DOM_PROPERTIES.has(attrName)) {
621
+ domOps.setProperty(el, attrName, value);
622
+ return;
814
623
  }
815
- return renderNodeH(result, ctx);
624
+ domOps.setAttribute(el, attrName, String(value));
816
625
  }
817
- async function renderNodeToHydratableString(node) {
818
- const ctx = createHydrationContext();
819
- return renderNodeH(node, ctx);
626
+ function applyStyle(el, styles) {
627
+ for (const [prop, val] of Object.entries(styles)) {
628
+ if (prop.includes("-")) {
629
+ el.style.setProperty(prop, val);
630
+ } else {
631
+ el.style[prop] = val;
632
+ }
633
+ }
820
634
  }
821
- var VOID_ELEMENTS4 = new Set([
635
+ function applyClass(el, value) {
636
+ let classStr;
637
+ if (Array.isArray(value)) {
638
+ classStr = value.filter(Boolean).join(" ");
639
+ } else if (typeof value === "object" && value !== null) {
640
+ classStr = Object.entries(value).filter(([, v]) => Boolean(v)).map(([k]) => k).join(" ");
641
+ } else {
642
+ classStr = String(value);
643
+ }
644
+ domOps.setAttribute(el, "class", classStr);
645
+ }
646
+
647
+ // src/component/control-flow.ts
648
+ var SHOW_TYPE = Symbol.for("Sinwan.Show");
649
+ var FOR_TYPE = Symbol.for("Sinwan.For");
650
+ function Show(props) {
651
+ return {
652
+ tag: SHOW_TYPE,
653
+ props,
654
+ children: []
655
+ };
656
+ }
657
+ function For(props) {
658
+ return {
659
+ tag: FOR_TYPE,
660
+ props,
661
+ children: []
662
+ };
663
+ }
664
+ function isShowElement(element) {
665
+ return element.tag === SHOW_TYPE;
666
+ }
667
+ function isForElement(element) {
668
+ return element.tag === FOR_TYPE;
669
+ }
670
+
671
+ // src/renderer/unmount.ts
672
+ function getMountedDomNodes(node) {
673
+ switch (node.type) {
674
+ case "text":
675
+ case "reactive-text":
676
+ return [node.node];
677
+ case "element":
678
+ return [node.node];
679
+ case "fragment":
680
+ return [
681
+ node.anchor,
682
+ ...node.children.flatMap((child) => getMountedDomNodes(child))
683
+ ];
684
+ case "reactive-block":
685
+ return [
686
+ node.startAnchor,
687
+ ...node.children.flatMap((child) => getMountedDomNodes(child)),
688
+ node.endAnchor
689
+ ];
690
+ case "component":
691
+ return node.children.flatMap((child) => getMountedDomNodes(child));
692
+ }
693
+ }
694
+ function unmountNode(node) {
695
+ switch (node.type) {
696
+ case "text":
697
+ break;
698
+ case "reactive-text":
699
+ node.dispose();
700
+ break;
701
+ case "element":
702
+ for (const dispose of node.attrDisposers) {
703
+ dispose();
704
+ }
705
+ for (const cleanup of node.eventCleanups) {
706
+ cleanup();
707
+ }
708
+ node.refCleanup?.();
709
+ for (const child of node.children) {
710
+ unmountNode(child);
711
+ }
712
+ break;
713
+ case "fragment":
714
+ for (const child of node.children) {
715
+ unmountNode(child);
716
+ }
717
+ break;
718
+ case "reactive-block":
719
+ node.dispose();
720
+ for (const child of node.children) {
721
+ unmountNode(child);
722
+ }
723
+ break;
724
+ case "component":
725
+ if (node.instance) {
726
+ fireUnmountedHooks(node.instance);
727
+ } else {
728
+ for (const dispose of node.disposers) {
729
+ dispose();
730
+ }
731
+ }
732
+ for (const child of node.children) {
733
+ unmountNode(child);
734
+ }
735
+ break;
736
+ }
737
+ }
738
+ function removeMountedNode(node) {
739
+ const domNodes = getMountedDomNodes(node);
740
+ unmountNode(node);
741
+ for (const domNode of domNodes) {
742
+ if (domNode.parentNode) {
743
+ domOps.remove(domNode);
744
+ }
745
+ }
746
+ }
747
+
748
+ // src/renderer/render-control-flow.ts
749
+ function renderControlFlowToDOM(element, parent, anchor, namespace) {
750
+ const startAnchor = domOps.createComment("Sinwan-b");
751
+ const endAnchor = domOps.createComment("/Sinwan-b");
752
+ insertNode(parent, startAnchor, anchor);
753
+ insertNode(parent, endAnchor, anchor);
754
+ const owner = getCurrentInstance();
755
+ let disposeEffect = () => {};
756
+ const block = {
757
+ type: "reactive-block",
758
+ dispose: () => disposeEffect(),
759
+ children: [],
760
+ startAnchor,
761
+ endAnchor
762
+ };
763
+ if (isShowElement(element)) {
764
+ disposeEffect = renderShowBlock(element, block, parent, namespace, owner);
765
+ } else if (isForElement(element)) {
766
+ disposeEffect = renderForBlock(element, block, parent, namespace, owner);
767
+ }
768
+ return block;
769
+ }
770
+ function renderShowBlock(element, block, parent, namespace, owner) {
771
+ let initialized = false;
772
+ return effect(() => {
773
+ clearChildren(block);
774
+ const when = readReactive(element.props.when);
775
+ const content = withOptionalInstance(owner, () => when ? resolveShowChildren(element, when) : element.props.fallback);
776
+ block.children = renderBlockContent(content, parent, block.endAnchor, namespace, owner);
777
+ if (initialized) {
778
+ fireMountedAndQueueUpdated(owner);
779
+ }
780
+ initialized = true;
781
+ });
782
+ }
783
+ function renderForBlock(element, block, parent, namespace, owner) {
784
+ let initialized = false;
785
+ let records = [];
786
+ return effect(() => {
787
+ const props = element.props;
788
+ const items = readReactive(props.each);
789
+ const list = Array.isArray(items) ? items : [];
790
+ const renderChild = props.children;
791
+ if (typeof renderChild !== "function") {
792
+ clearChildren(block);
793
+ records = [];
794
+ if (initialized) {
795
+ queueUpdatedHooks(owner);
796
+ }
797
+ initialized = true;
798
+ return;
799
+ }
800
+ const oldByKey = new Map;
801
+ for (const record of records) {
802
+ oldByKey.set(record.key, record);
803
+ }
804
+ const nextRecords = [];
805
+ list.forEach((item, index) => {
806
+ const key = props.key ? props.key(item, index) : item;
807
+ const old = oldByKey.get(key);
808
+ if (old && old.item === item) {
809
+ old.index = index;
810
+ moveBeforeEnd(parent, old.mounted, block.endAnchor);
811
+ nextRecords.push(old);
812
+ oldByKey.delete(key);
813
+ return;
814
+ }
815
+ if (old) {
816
+ removeMountedNode(old.mounted);
817
+ oldByKey.delete(key);
818
+ }
819
+ const record = {
820
+ key,
821
+ item,
822
+ index,
823
+ mounted: { type: "text", node: domOps.createTextNode("") }
824
+ };
825
+ record.mounted = withOptionalInstance(owner, () => renderNodeToDOM(renderChild(item, () => record.index), parent, block.endAnchor, namespace));
826
+ nextRecords.push(record);
827
+ });
828
+ for (const record of oldByKey.values()) {
829
+ removeMountedNode(record.mounted);
830
+ }
831
+ records = nextRecords;
832
+ block.children = nextRecords.map((record) => record.mounted);
833
+ if (initialized) {
834
+ fireMountedAndQueueUpdated(owner);
835
+ }
836
+ initialized = true;
837
+ });
838
+ }
839
+ function resolveShowChildren(element, value) {
840
+ const children = element.props.children ?? element.children;
841
+ if (typeof children === "function") {
842
+ return children(value);
843
+ }
844
+ return children;
845
+ }
846
+ function renderBlockContent(content, parent, anchor, namespace, owner) {
847
+ if (content == null || typeof content === "boolean") {
848
+ return [];
849
+ }
850
+ const nodes = Array.isArray(content) ? content : [content];
851
+ return nodes.map((node) => withOptionalInstance(owner, () => renderNodeToDOM(node, parent, anchor, namespace)));
852
+ }
853
+ function clearChildren(block) {
854
+ for (const child of block.children) {
855
+ removeMountedNode(child);
856
+ }
857
+ block.children = [];
858
+ }
859
+ function moveBeforeEnd(parent, mounted, endAnchor) {
860
+ for (const node of getMountedDomNodes(mounted)) {
861
+ domOps.insertBefore(parent, node, endAnchor);
862
+ }
863
+ }
864
+ function fireMountedAndQueueUpdated(owner) {
865
+ if (owner) {
866
+ fireMountedHooks(owner);
867
+ }
868
+ queueUpdatedHooks(owner);
869
+ }
870
+ function withOptionalInstance(owner, fn) {
871
+ return owner ? withInstance(owner, fn) : fn();
872
+ }
873
+ function readReactive(value) {
874
+ return isSignal(value) || isComputed(value) ? value.value : value;
875
+ }
876
+ function insertNode(parent, child, anchor) {
877
+ if (anchor) {
878
+ domOps.insertBefore(parent, child, anchor);
879
+ } else {
880
+ domOps.appendChild(parent, child);
881
+ }
882
+ }
883
+
884
+ // src/renderer/render-element.ts
885
+ var VOID_ELEMENTS2 = new Set([
822
886
  "area",
823
887
  "base",
824
888
  "br",
@@ -834,298 +898,824 @@ var VOID_ELEMENTS4 = new Set([
834
898
  "track",
835
899
  "wbr"
836
900
  ]);
837
- function renderNodeH(node, ctx) {
838
- if (node == null || typeof node === "boolean")
839
- return "";
840
- if (typeof node === "string")
841
- return escapeHtml(node);
842
- if (typeof node === "number")
843
- return String(node);
844
- if (node instanceof HtmlEscapedString)
845
- return node.value;
846
- if (isSignal(node) || isComputed(node)) {
847
- const value = node.value;
848
- const idx = ctx.textIndex++;
849
- return `${textMarkerOpen(idx)}${escapeHtml(String(value))}${textMarkerCloseStr()}`;
901
+ var SVG_NS = "http://www.w3.org/2000/svg";
902
+ var MATH_NS = "http://www.w3.org/1998/Math/MathML";
903
+ function renderElementToDOM(element, parent, anchor = null, namespace = null) {
904
+ const { tag, props, children } = element;
905
+ if (tag === "" || tag === Fragment) {
906
+ return renderFragmentToDOM(children, parent, anchor, namespace);
850
907
  }
851
- if (Array.isArray(node)) {
852
- return node.map((child) => renderNodeH(child, ctx)).join("");
908
+ if (tag === Show || tag === For) {
909
+ return renderElementToDOM(tag(props), parent, anchor, namespace);
853
910
  }
854
- if (node instanceof Promise) {
855
- return "";
911
+ if (isShowElement(element) || isForElement(element)) {
912
+ return renderControlFlowToDOM(element, parent, anchor, namespace);
856
913
  }
857
- if (typeof node === "object" && "tag" in node) {
858
- return renderElementH(node, ctx, false);
914
+ if (typeof tag === "function") {
915
+ return renderComponentToDOM(tag, props, parent, anchor, namespace);
859
916
  }
860
- return escapeHtml(String(node));
917
+ if (typeof tag === "string") {
918
+ return renderIntrinsicToDOM(tag, props, children, parent, anchor, namespace);
919
+ }
920
+ return renderFragmentToDOM(children, parent, anchor, namespace);
921
+ }
922
+ function renderIntrinsicToDOM(tag, props, children, parent, anchor, parentNamespace) {
923
+ const namespace = getElementNamespace(tag, parentNamespace);
924
+ const el = namespace ? domOps.createElementNS(namespace, tag) : domOps.createElement(tag);
925
+ const attrDisposers = applyAttributes(el, props);
926
+ const eventCleanups = bindEvents(el, props);
927
+ let mountedChildren = [];
928
+ if (!VOID_ELEMENTS2.has(tag)) {
929
+ const dangerous = props.dangerouslySetInnerHTML;
930
+ if (dangerous && typeof dangerous.__html === "string") {
931
+ el.innerHTML = dangerous.__html;
932
+ } else {
933
+ mountedChildren = renderChildrenToDOM(children, el, getChildNamespace(tag, namespace));
934
+ }
935
+ }
936
+ if (anchor) {
937
+ domOps.insertBefore(parent, el, anchor);
938
+ } else {
939
+ domOps.appendChild(parent, el);
940
+ }
941
+ const refCleanup = applyRef(el, props.ref);
942
+ return {
943
+ type: "element",
944
+ node: el,
945
+ children: mountedChildren,
946
+ eventCleanups,
947
+ attrDisposers,
948
+ refCleanup
949
+ };
950
+ }
951
+ function renderComponentToDOM(component, props, parent, anchor, namespace) {
952
+ const parentInstance = getCurrentInstance();
953
+ const instance = createComponentInstance(component, props, parentInstance);
954
+ if (parentInstance) {
955
+ parentInstance.children.push(instance);
956
+ }
957
+ const prevInstance = setCurrentInstance(instance);
958
+ let result;
959
+ let child;
960
+ try {
961
+ result = component(props);
962
+ if (result && typeof result === "object" && "tag" in result) {
963
+ child = renderElementToDOM(result, parent, anchor, namespace);
964
+ } else {
965
+ child = renderNodeToDOM(result, parent, anchor, namespace);
966
+ }
967
+ } catch (err) {
968
+ setCurrentInstance(prevInstance);
969
+ handleComponentError(instance, err);
970
+ const text = domOps.createTextNode("");
971
+ if (anchor) {
972
+ domOps.insertBefore(parent, text, anchor);
973
+ } else {
974
+ domOps.appendChild(parent, text);
975
+ }
976
+ return {
977
+ type: "component",
978
+ children: [{ type: "text", node: text }],
979
+ disposers: [],
980
+ instance
981
+ };
982
+ }
983
+ setCurrentInstance(prevInstance);
984
+ instance.element = child;
985
+ return {
986
+ type: "component",
987
+ children: [child],
988
+ disposers: instance.effects,
989
+ instance
990
+ };
991
+ }
992
+ function renderFragmentToDOM(children, parent, anchor, namespace) {
993
+ const anchorComment = domOps.createComment("Sinwan-f");
994
+ if (anchor) {
995
+ domOps.insertBefore(parent, anchorComment, anchor);
996
+ } else {
997
+ domOps.appendChild(parent, anchorComment);
998
+ }
999
+ const mounted = [];
1000
+ for (const child of children) {
1001
+ mounted.push(renderNodeToDOM(child, parent, anchor, namespace));
1002
+ }
1003
+ return { type: "fragment", children: mounted, anchor: anchorComment };
1004
+ }
1005
+ function getElementNamespace(tag, parentNamespace) {
1006
+ if (tag === "svg")
1007
+ return SVG_NS;
1008
+ if (tag === "math")
1009
+ return MATH_NS;
1010
+ return parentNamespace;
1011
+ }
1012
+ function getChildNamespace(tag, namespace) {
1013
+ if (namespace === SVG_NS && tag === "foreignObject") {
1014
+ return null;
1015
+ }
1016
+ return namespace;
1017
+ }
1018
+ function applyRef(el, ref) {
1019
+ const value = ref;
1020
+ if (!value) {
1021
+ return null;
1022
+ }
1023
+ if (typeof value === "function") {
1024
+ value(el);
1025
+ return () => value(null);
1026
+ }
1027
+ if (typeof value === "object" && "current" in value) {
1028
+ value.current = el;
1029
+ return () => {
1030
+ value.current = null;
1031
+ };
1032
+ }
1033
+ return null;
1034
+ }
1035
+
1036
+ // src/renderer/render-children.ts
1037
+ function renderNodeToDOM(node, parent, anchor = null, namespace = null) {
1038
+ if (node == null || typeof node === "boolean") {
1039
+ const text2 = domOps.createTextNode("");
1040
+ insertNode2(parent, text2, anchor);
1041
+ return { type: "text", node: text2 };
1042
+ }
1043
+ if (typeof node === "string") {
1044
+ const text2 = domOps.createTextNode(node);
1045
+ insertNode2(parent, text2, anchor);
1046
+ return { type: "text", node: text2 };
1047
+ }
1048
+ if (typeof node === "number") {
1049
+ const text2 = domOps.createTextNode(String(node));
1050
+ insertNode2(parent, text2, anchor);
1051
+ return { type: "text", node: text2 };
1052
+ }
1053
+ if (node instanceof HtmlEscapedString) {
1054
+ const text2 = domOps.createTextNode(node.value);
1055
+ insertNode2(parent, text2, anchor);
1056
+ return { type: "text", node: text2 };
1057
+ }
1058
+ if (isSignal(node) || isComputed(node)) {
1059
+ const text2 = domOps.createTextNode(String(node.value));
1060
+ insertNode2(parent, text2, anchor);
1061
+ const owner = getCurrentInstance();
1062
+ let initialized = false;
1063
+ const dispose = effect(() => {
1064
+ domOps.setTextContent(text2, String(node.value));
1065
+ if (initialized) {
1066
+ queueUpdatedHooks(owner);
1067
+ }
1068
+ initialized = true;
1069
+ });
1070
+ return { type: "reactive-text", node: text2, dispose };
1071
+ }
1072
+ if (Array.isArray(node)) {
1073
+ return renderArrayToDOM(node, parent, anchor, namespace);
1074
+ }
1075
+ if (node instanceof Promise) {
1076
+ const placeholder = domOps.createTextNode("");
1077
+ insertNode2(parent, placeholder, anchor);
1078
+ node.then((resolved) => {
1079
+ const mounted = renderNodeToDOM(resolved, parent, placeholder, namespace);
1080
+ domOps.remove(placeholder);
1081
+ });
1082
+ return { type: "text", node: placeholder };
1083
+ }
1084
+ if (typeof node === "object" && "tag" in node) {
1085
+ return renderElementToDOM(node, parent, anchor, namespace);
1086
+ }
1087
+ const text = domOps.createTextNode(String(node));
1088
+ insertNode2(parent, text, anchor);
1089
+ return { type: "text", node: text };
1090
+ }
1091
+ function renderArrayToDOM(nodes, parent, anchor, namespace) {
1092
+ const anchorComment = domOps.createComment("Sinwan-f");
1093
+ insertNode2(parent, anchorComment, anchor);
1094
+ const children = [];
1095
+ for (const child of nodes) {
1096
+ children.push(renderNodeToDOM(child, parent, anchor, namespace));
1097
+ }
1098
+ return { type: "fragment", children, anchor: anchorComment };
1099
+ }
1100
+ function renderChildrenToDOM(children, parent, namespace = null) {
1101
+ const mounted = [];
1102
+ for (const child of children) {
1103
+ mounted.push(renderNodeToDOM(child, parent, null, namespace));
1104
+ }
1105
+ return mounted;
1106
+ }
1107
+ function insertNode2(parent, child, anchor) {
1108
+ if (anchor) {
1109
+ domOps.insertBefore(parent, child, anchor);
1110
+ } else {
1111
+ domOps.appendChild(parent, child);
1112
+ }
1113
+ }
1114
+
1115
+ // src/renderer/mount.ts
1116
+ function mount(component, container, props) {
1117
+ container.innerHTML = "";
1118
+ const mergedProps = props ?? {};
1119
+ const instance = createComponentInstance(component, mergedProps, null);
1120
+ let result;
1121
+ let root;
1122
+ setCurrentInstance(instance);
1123
+ try {
1124
+ result = component(mergedProps);
1125
+ if (result instanceof Promise) {
1126
+ const placeholder = document.createTextNode("");
1127
+ container.appendChild(placeholder);
1128
+ root = { type: "text", node: placeholder };
1129
+ result.then((resolved) => {
1130
+ container.innerHTML = "";
1131
+ setCurrentInstance(instance);
1132
+ root = renderElementToDOM(resolved, container);
1133
+ setCurrentInstance(null);
1134
+ instance.element = root;
1135
+ fireMountedHooks(instance);
1136
+ });
1137
+ } else if (result && typeof result === "object" && "tag" in result) {
1138
+ root = renderElementToDOM(result, container);
1139
+ } else {
1140
+ root = renderNodeToDOM(result, container);
1141
+ }
1142
+ } catch (err) {
1143
+ setCurrentInstance(null);
1144
+ handleComponentError(instance, err);
1145
+ return {
1146
+ root: { type: "text", node: document.createTextNode("") },
1147
+ unmount() {}
1148
+ };
1149
+ }
1150
+ setCurrentInstance(null);
1151
+ instance.element = root;
1152
+ fireMountedHooks(instance);
1153
+ return {
1154
+ root,
1155
+ unmount() {
1156
+ fireUnmountedHooks(instance);
1157
+ unmountNode(root);
1158
+ container.innerHTML = "";
1159
+ }
1160
+ };
1161
+ }
1162
+ function render(node, container) {
1163
+ container.innerHTML = "";
1164
+ const root = renderNodeToDOM(node, container);
1165
+ return {
1166
+ root,
1167
+ unmount() {
1168
+ unmountNode(root);
1169
+ container.innerHTML = "";
1170
+ }
1171
+ };
1172
+ }
1173
+ // src/server/index.ts
1174
+ var exports_server = {};
1175
+ __export(exports_server, {
1176
+ streamPage: () => streamPage,
1177
+ streamHydratablePage: () => streamHydratablePage,
1178
+ streamHydratableNode: () => streamHydratableNode,
1179
+ renderToString: () => renderToString,
1180
+ renderToHydratableString: () => renderToHydratableString,
1181
+ renderPage: () => renderPage,
1182
+ renderNodeToHydratableString: () => renderNodeToHydratableString,
1183
+ registerPage: () => registerPage,
1184
+ isSlots: () => isSlots,
1185
+ hasPage: () => hasPage,
1186
+ getPage: () => getPage
1187
+ });
1188
+ module.exports = __toCommonJS(exports_server);
1189
+
1190
+ // src/escaper.ts
1191
+ var _bun = globalThis.Bun;
1192
+ var _nativeEscape = typeof _bun?.escapeHTML === "function" ? _bun.escapeHTML.bind(_bun) : undefined;
1193
+ var HTML_ESCAPE_RE = /[&<>"']/g;
1194
+ var HTML_ESCAPE_MAP = {
1195
+ "&": "&amp;",
1196
+ "<": "&lt;",
1197
+ ">": "&gt;",
1198
+ '"': "&quot;",
1199
+ "'": "&#39;"
1200
+ };
1201
+ function portableEscape(str) {
1202
+ HTML_ESCAPE_RE.lastIndex = 0;
1203
+ if (!HTML_ESCAPE_RE.test(str))
1204
+ return str;
1205
+ return str.replace(HTML_ESCAPE_RE, (c) => HTML_ESCAPE_MAP[c]);
1206
+ }
1207
+ function escapeHtml(value) {
1208
+ if (value == null || typeof value === "boolean")
1209
+ return "";
1210
+ if (typeof value === "number")
1211
+ return String(value);
1212
+ if (value instanceof HtmlEscapedString)
1213
+ return value.value;
1214
+ const s = String(value);
1215
+ return _nativeEscape ? _nativeEscape(s) : portableEscape(s);
1216
+ }
1217
+ function safeHtml(html) {
1218
+ return raw(html);
1219
+ }
1220
+ function isSafeHtml(value) {
1221
+ return value instanceof HtmlEscapedString;
1222
+ }
1223
+
1224
+ // src/server/renderer.ts
1225
+ var componentCache = new WeakMap;
1226
+ var pageRegistry = new Map;
1227
+ function registerPage(name, page) {
1228
+ pageRegistry.set(name, page);
1229
+ }
1230
+ function getPage(name) {
1231
+ return pageRegistry.get(name);
1232
+ }
1233
+ function hasPage(name) {
1234
+ return pageRegistry.has(name);
1235
+ }
1236
+ async function renderPage(name, data) {
1237
+ const page = getPage(name);
1238
+ if (!page) {
1239
+ throw new Error(`Page "${name}" not found in registry`);
1240
+ }
1241
+ const element = await page(data);
1242
+ return renderToString(element);
1243
+ }
1244
+ async function renderToString(node) {
1245
+ if (node == null || typeof node === "boolean") {
1246
+ return "";
1247
+ }
1248
+ if (typeof node === "string") {
1249
+ return escapeHtml(node);
1250
+ }
1251
+ if (typeof node === "number") {
1252
+ return String(node);
1253
+ }
1254
+ if (node instanceof HtmlEscapedString) {
1255
+ return node.value;
1256
+ }
1257
+ if (isSignal(node) || isComputed(node)) {
1258
+ return escapeHtml(String(node.value));
1259
+ }
1260
+ if (Array.isArray(node)) {
1261
+ const results = await Promise.all(node.map((child) => renderToString(child)));
1262
+ return results.join("");
1263
+ }
1264
+ if (node instanceof Promise) {
1265
+ return renderElement(await node);
1266
+ }
1267
+ return renderElement(node);
1268
+ }
1269
+ async function renderElement(element) {
1270
+ const { tag, props, children } = element;
1271
+ if (isShowElement(element)) {
1272
+ const when = readReactive2(props.when);
1273
+ return renderToString(when ? resolveShowChildren2(element, when) : props.fallback);
1274
+ }
1275
+ if (isForElement(element)) {
1276
+ return renderForElement(element);
1277
+ }
1278
+ if (typeof tag === "function") {
1279
+ const result = await tag(props);
1280
+ return renderToString(result);
1281
+ }
1282
+ if (typeof tag === "string") {
1283
+ return renderIntrinsicElement(tag, props, children);
1284
+ }
1285
+ return renderToString(children);
1286
+ }
1287
+ var VOID_ELEMENTS3 = new Set([
1288
+ "area",
1289
+ "base",
1290
+ "br",
1291
+ "col",
1292
+ "embed",
1293
+ "hr",
1294
+ "img",
1295
+ "input",
1296
+ "link",
1297
+ "meta",
1298
+ "param",
1299
+ "source",
1300
+ "track",
1301
+ "wbr"
1302
+ ]);
1303
+ async function renderIntrinsicElement(tag, props, children) {
1304
+ const attrs = renderAttributes(props);
1305
+ if (VOID_ELEMENTS3.has(tag)) {
1306
+ return attrs ? `<${tag}${attrs}>` : `<${tag}>`;
1307
+ }
1308
+ const childrenHtml = await renderChildren(children, props);
1309
+ return attrs ? `<${tag}${attrs}>${childrenHtml}</${tag}>` : `<${tag}>${childrenHtml}</${tag}>`;
1310
+ }
1311
+ function renderAttributes(props) {
1312
+ let attrs = "";
1313
+ for (const [key, value] of Object.entries(props)) {
1314
+ if (key === "children" || key === "key" || key === "ref" || key === "dangerouslySetInnerHTML" || isEventProp(key)) {
1315
+ continue;
1316
+ }
1317
+ const resolvedValue = readReactive2(value);
1318
+ if (resolvedValue == null || resolvedValue === false)
1319
+ continue;
1320
+ const attrName = key === "className" ? "class" : key;
1321
+ const finalName = attrName === "htmlFor" ? "for" : attrName;
1322
+ if (resolvedValue === true) {
1323
+ attrs += ` ${finalName}`;
1324
+ continue;
1325
+ }
1326
+ const attrValue = escapeHtml(String(resolvedValue));
1327
+ attrs += ` ${finalName}="${attrValue}"`;
1328
+ }
1329
+ return attrs;
1330
+ }
1331
+ async function renderChildren(children, props) {
1332
+ const dangerous = props.dangerouslySetInnerHTML;
1333
+ if (dangerous && typeof dangerous.__html === "string") {
1334
+ return dangerous.__html;
1335
+ }
1336
+ return renderToString(children);
1337
+ }
1338
+ function isSlots(children) {
1339
+ return children != null && typeof children === "object" && !Array.isArray(children) && !(children instanceof HtmlEscapedString);
1340
+ }
1341
+ async function renderForElement(element) {
1342
+ const props = element.props;
1343
+ const each = readReactive2(props.each);
1344
+ if (!Array.isArray(each) || typeof props.children !== "function") {
1345
+ return "";
1346
+ }
1347
+ const rendered = await Promise.all(each.map((item, index) => renderToString(props.children(item, () => index))));
1348
+ return rendered.join("");
1349
+ }
1350
+ function resolveShowChildren2(element, value) {
1351
+ const children = element.props.children ?? element.children;
1352
+ if (typeof children === "function") {
1353
+ return children(value);
1354
+ }
1355
+ return children;
1356
+ }
1357
+ function readReactive2(value) {
1358
+ return isSignal(value) || isComputed(value) ? value.value : value;
1359
+ }
1360
+ // src/hydration/markers.ts
1361
+ var COMP_ID_ATTR = "data-sinwan-id";
1362
+ var COMP_ID_PREFIX = "c";
1363
+ var TEXT_MARKER_OPEN = "sinwan-t:";
1364
+ var TEXT_MARKER_CLOSE = "/sinwan-t";
1365
+ var EVENT_ATTR = "data-sinwan-ev";
1366
+ function compId(index) {
1367
+ return `${COMP_ID_PREFIX}${index}`;
1368
+ }
1369
+ function textMarkerOpen(index) {
1370
+ return `<!--${TEXT_MARKER_OPEN}${index}-->`;
1371
+ }
1372
+ function textMarkerCloseStr() {
1373
+ return `<!--${TEXT_MARKER_CLOSE}-->`;
1374
+ }
1375
+ function parseTextOpenMarker(node) {
1376
+ const data = node.data;
1377
+ if (data.startsWith(TEXT_MARKER_OPEN)) {
1378
+ const idx = parseInt(data.slice(TEXT_MARKER_OPEN.length), 10);
1379
+ return Number.isNaN(idx) ? -1 : idx;
1380
+ }
1381
+ return -1;
1382
+ }
1383
+ function isTextCloseMarker(node) {
1384
+ return node.data === TEXT_MARKER_CLOSE;
1385
+ }
1386
+
1387
+ // src/server/stream.ts
1388
+ function createHydratableStreamContext() {
1389
+ return { componentIndex: 0, textIndex: 0, eventIndex: 0 };
1390
+ }
1391
+ var VOID_ELEMENTS4 = new Set([
1392
+ "area",
1393
+ "base",
1394
+ "br",
1395
+ "col",
1396
+ "embed",
1397
+ "hr",
1398
+ "img",
1399
+ "input",
1400
+ "link",
1401
+ "meta",
1402
+ "param",
1403
+ "source",
1404
+ "track",
1405
+ "wbr"
1406
+ ]);
1407
+ function streamPage(page, data) {
1408
+ const encoder = new TextEncoder;
1409
+ return new ReadableStream({
1410
+ async start(controller) {
1411
+ try {
1412
+ const element = await page(data);
1413
+ await streamNode(element, controller, encoder);
1414
+ controller.close();
1415
+ } catch (error) {
1416
+ controller.error(error);
1417
+ }
1418
+ }
1419
+ });
1420
+ }
1421
+ function streamHydratablePage(component, props) {
1422
+ const encoder = new TextEncoder;
1423
+ const ctx = createHydratableStreamContext();
1424
+ return new ReadableStream({
1425
+ async start(controller) {
1426
+ try {
1427
+ await streamHydratableComponent(component, props ?? {}, controller, encoder, ctx, true);
1428
+ controller.close();
1429
+ } catch (error) {
1430
+ controller.error(error);
1431
+ }
1432
+ }
1433
+ });
1434
+ }
1435
+ function streamHydratableNode(node) {
1436
+ const encoder = new TextEncoder;
1437
+ const ctx = createHydratableStreamContext();
1438
+ return new ReadableStream({
1439
+ async start(controller) {
1440
+ try {
1441
+ await streamHydratableNodeToController(node, controller, encoder, ctx);
1442
+ controller.close();
1443
+ } catch (error) {
1444
+ controller.error(error);
1445
+ }
1446
+ }
1447
+ });
861
1448
  }
862
- function renderElementH(element, ctx, isComponentRoot) {
1449
+ async function streamNode(node, controller, encoder) {
1450
+ if (node == null || typeof node === "boolean") {
1451
+ return;
1452
+ }
1453
+ if (typeof node === "string") {
1454
+ controller.enqueue(encoder.encode(escapeHtml(node)));
1455
+ return;
1456
+ }
1457
+ if (typeof node === "number") {
1458
+ controller.enqueue(encoder.encode(String(node)));
1459
+ return;
1460
+ }
1461
+ if (node instanceof HtmlEscapedString) {
1462
+ controller.enqueue(encoder.encode(node.value));
1463
+ return;
1464
+ }
1465
+ if (isSignal(node) || isComputed(node)) {
1466
+ controller.enqueue(encoder.encode(escapeHtml(String(node.value))));
1467
+ return;
1468
+ }
1469
+ if (Array.isArray(node)) {
1470
+ for (const child of node) {
1471
+ await streamNode(child, controller, encoder);
1472
+ }
1473
+ return;
1474
+ }
1475
+ if (node instanceof Promise) {
1476
+ const resolved = await node;
1477
+ await streamElement(resolved, controller, encoder);
1478
+ return;
1479
+ }
1480
+ await streamElement(node, controller, encoder);
1481
+ }
1482
+ async function streamElement(element, controller, encoder) {
863
1483
  const { tag, props, children } = element;
864
- if (tag === "") {
865
- return children.map((child) => renderNodeH(child, ctx)).join("");
1484
+ if (isShowElement(element)) {
1485
+ const when = readReactive3(props.when);
1486
+ await streamNode(when ? resolveShowChildren3(element, when) : props.fallback, controller, encoder);
1487
+ return;
1488
+ }
1489
+ if (isForElement(element)) {
1490
+ await streamForElement(element, controller, encoder);
1491
+ return;
866
1492
  }
867
1493
  if (typeof tag === "function") {
868
- return renderComponentH(tag, props, ctx);
1494
+ const result = await tag(props);
1495
+ await streamNode(result, controller, encoder);
1496
+ return;
869
1497
  }
870
1498
  if (typeof tag === "string") {
871
- return renderIntrinsicH(tag, props, children, ctx, isComponentRoot);
1499
+ await streamIntrinsicElement(tag, props, children, controller, encoder);
1500
+ return;
872
1501
  }
873
- return children.map((child) => renderNodeH(child, ctx)).join("");
1502
+ await streamNode(children, controller, encoder);
874
1503
  }
875
- function renderComponentH(component, props, ctx) {
876
- const parentInstance = globalThis.__SinwanCurrentInstance;
877
- const instance = createComponentInstance(component, props, null);
878
- const prev = setCurrentInstance(instance);
879
- const result = component(props);
880
- setCurrentInstance(prev);
881
- if (result && typeof result === "object" && "tag" in result) {
882
- return renderElementH(result, ctx, true);
1504
+ async function streamIntrinsicElement(tag, props, children, controller, encoder) {
1505
+ const attrs = renderAttributes2(props);
1506
+ const dangerous = props.dangerouslySetInnerHTML;
1507
+ if (VOID_ELEMENTS4.has(tag)) {
1508
+ const html = attrs ? `<${tag}${attrs}>` : `<${tag}>`;
1509
+ controller.enqueue(encoder.encode(html));
1510
+ return;
1511
+ }
1512
+ const openTag = attrs ? `<${tag}${attrs}>` : `<${tag}>`;
1513
+ controller.enqueue(encoder.encode(openTag));
1514
+ if (dangerous && typeof dangerous.__html === "string") {
1515
+ controller.enqueue(encoder.encode(dangerous.__html));
1516
+ } else {
1517
+ await streamNode(children, controller, encoder);
883
1518
  }
884
- return renderNodeH(result, ctx);
1519
+ controller.enqueue(encoder.encode(`</${tag}>`));
885
1520
  }
886
- function renderIntrinsicH(tag, props, children, ctx, isComponentRoot) {
1521
+ function renderAttributes2(props) {
887
1522
  let attrs = "";
888
- if (isComponentRoot) {
889
- attrs += ` ${COMP_ID_ATTR}="${compId(ctx.componentIndex++)}"`;
890
- }
891
- const eventParts = [];
892
1523
  for (const [key, value] of Object.entries(props)) {
893
- if (key === "children" || key === "dangerouslySetInnerHTML")
894
- continue;
895
- if (isEventProp(key)) {
896
- const eventName = toEventName(key);
897
- eventParts.push(`${eventName}:${ctx.eventIndex++}`);
1524
+ if (key === "children" || key === "key" || key === "ref" || key === "dangerouslySetInnerHTML" || isEventProp(key)) {
898
1525
  continue;
899
1526
  }
900
- if (value == null || value === false)
1527
+ const resolvedValue = readReactive3(value);
1528
+ if (resolvedValue == null || resolvedValue === false)
901
1529
  continue;
902
- let resolvedValue = value;
903
- if (isSignal(value) || isComputed(value)) {
904
- resolvedValue = value.value;
905
- }
1530
+ const attrName = key === "className" ? "class" : key === "htmlFor" ? "for" : key;
906
1531
  if (resolvedValue === true) {
907
- const attrName2 = key === "className" ? "class" : key === "htmlFor" ? "for" : key;
908
- attrs += ` ${attrName2}`;
1532
+ attrs += ` ${attrName}`;
909
1533
  continue;
910
1534
  }
911
- const attrName = key === "className" ? "class" : key === "htmlFor" ? "for" : key;
912
- attrs += ` ${attrName}="${escapeHtml(String(resolvedValue))}"`;
913
- }
914
- if (eventParts.length > 0) {
915
- attrs += ` ${EVENT_ATTR}="${eventParts.join(",")}"`;
1535
+ const attrValue = escapeHtml(String(resolvedValue));
1536
+ attrs += ` ${attrName}="${attrValue}"`;
916
1537
  }
917
- if (VOID_ELEMENTS4.has(tag)) {
918
- return `<${tag}${attrs}>`;
1538
+ return attrs;
1539
+ }
1540
+ async function streamHydratableNodeToController(node, controller, encoder, ctx, isComponentRoot = false) {
1541
+ if (node == null || typeof node === "boolean") {
1542
+ return;
919
1543
  }
920
- const dangerous = props.dangerouslySetInnerHTML;
921
- if (dangerous && typeof dangerous.__html === "string") {
922
- return `<${tag}${attrs}>${dangerous.__html}</${tag}>`;
1544
+ if (typeof node === "string") {
1545
+ enqueue(controller, encoder, escapeHtml(node));
1546
+ return;
923
1547
  }
924
- const childrenHtml = children.map((child) => renderNodeH(child, ctx)).join("");
925
- return `<${tag}${attrs}>${childrenHtml}</${tag}>`;
926
- }
927
- // src/index.ts
928
- var exports_src = {};
929
- __export(exports_src, {
930
- unmountNode: () => unmountNode,
931
- streamPage: () => streamPage,
932
- signal: () => signal,
933
- safeHtml: () => safeHtml,
934
- renderToString: () => renderToString,
935
- renderToHydratableString: () => renderToHydratableString,
936
- renderPage: () => renderPage,
937
- renderNodeToHydratableString: () => renderNodeToHydratableString,
938
- renderNodeToDOM: () => renderNodeToDOM,
939
- renderElementToDOM: () => renderElementToDOM,
940
- render: () => render,
941
- registerPage: () => registerPage,
942
- raw: () => raw,
943
- provide: () => provide,
944
- onUpdated: () => onUpdated,
945
- onUnmounted: () => onUnmounted,
946
- onMounted: () => onMounted,
947
- onError: () => onError,
948
- nextTick: () => nextTick,
949
- mount: () => mount,
950
- jsxs: () => jsxs,
951
- jsxDEV: () => jsxDEV,
952
- jsx: () => jsx,
953
- isSignal: () => isSignal,
954
- isSafeHtml: () => isSafeHtml,
955
- isComputed: () => isComputed,
956
- inject: () => inject,
957
- hydrate: () => hydrate,
958
- hasPage: () => hasPage,
959
- getPage: () => getPage,
960
- getCurrentInstance: () => getCurrentInstance,
961
- escapeHtml: () => escapeHtml,
962
- effect: () => effect,
963
- createPage: () => createPage,
964
- createLayout: () => createLayout,
965
- createComponent: () => createComponent,
966
- computed: () => computed,
967
- batch: () => batch,
968
- HtmlEscapedString: () => HtmlEscapedString,
969
- Fragment: () => Fragment
970
- });
971
- module.exports = __toCommonJS(exports_src);
972
- // src/reactivity/batch.ts
973
- var batchDepth = 0;
974
- function batch(fn) {
975
- batchDepth++;
976
- try {
977
- fn();
978
- } finally {
979
- batchDepth--;
980
- if (batchDepth === 0) {
981
- flushSync();
982
- }
1548
+ if (typeof node === "number") {
1549
+ enqueue(controller, encoder, String(node));
1550
+ return;
983
1551
  }
984
- }
985
- // src/component/lifecycle.ts
986
- function onMounted(fn) {
987
- const instance = getCurrentInstance();
988
- if (!instance) {
989
- throw new Error("onMounted() called outside of component setup.");
1552
+ if (node instanceof HtmlEscapedString) {
1553
+ enqueue(controller, encoder, node.value);
1554
+ return;
990
1555
  }
991
- instance._mountedHooks.push(fn);
992
- }
993
- function onUnmounted(fn) {
994
- const instance = getCurrentInstance();
995
- if (!instance) {
996
- throw new Error("onUnmounted() called outside of component setup.");
1556
+ if (isSignal(node) || isComputed(node)) {
1557
+ const idx = ctx.textIndex++;
1558
+ enqueue(controller, encoder, `${textMarkerOpen(idx)}${escapeHtml(String(node.value))}${textMarkerCloseStr()}`);
1559
+ return;
997
1560
  }
998
- instance._unmountedHooks.push(fn);
999
- }
1000
- function onUpdated(fn) {
1001
- const instance = getCurrentInstance();
1002
- if (!instance) {
1003
- throw new Error("onUpdated() called outside of component setup.");
1561
+ if (Array.isArray(node)) {
1562
+ for (const child of node) {
1563
+ await streamHydratableNodeToController(child, controller, encoder, ctx);
1564
+ }
1565
+ return;
1004
1566
  }
1005
- instance._updatedHooks.push(fn);
1006
- }
1007
- function onError(fn) {
1008
- const instance = getCurrentInstance();
1009
- if (!instance) {
1010
- throw new Error("onError() called outside of component setup.");
1567
+ if (node instanceof Promise) {
1568
+ await streamHydratableElement(await node, controller, encoder, ctx, isComponentRoot);
1569
+ return;
1011
1570
  }
1012
- instance._errorHooks.push(fn);
1013
- }
1014
- // src/component/create.ts
1015
- function createComponent(fn) {
1016
- const component = (props) => fn(props);
1017
- component._SinwanComponent = true;
1018
- component._displayName = fn.name || "AnonymousComponent";
1019
- return component;
1020
- }
1021
- function createPage(fn) {
1022
- const page = (data) => fn(data);
1023
- page._SinwanPage = true;
1024
- page._displayName = fn.name || "AnonymousPage";
1025
- return page;
1026
- }
1027
- function createLayout(fn) {
1028
- return createComponent(fn);
1571
+ await streamHydratableElement(node, controller, encoder, ctx, isComponentRoot);
1029
1572
  }
1030
- // src/component/provide-inject.ts
1031
- function provide(key, value) {
1032
- const instance = getCurrentInstance();
1033
- if (!instance) {
1034
- throw new Error("provide() called outside of component setup.");
1573
+ async function streamHydratableElement(element, controller, encoder, ctx, isComponentRoot) {
1574
+ const { tag, props, children } = element;
1575
+ if (tag === "") {
1576
+ for (const child of children) {
1577
+ await streamHydratableNodeToController(child, controller, encoder, ctx);
1578
+ }
1579
+ return;
1035
1580
  }
1036
- instance.provides[key] = value;
1037
- }
1038
- function inject(key, defaultValue) {
1039
- const instance = getCurrentInstance();
1040
- if (!instance) {
1041
- throw new Error("inject() called outside of component setup.");
1581
+ if (tag === Show || tag === For) {
1582
+ await streamHydratableElement(tag(props), controller, encoder, ctx, isComponentRoot);
1583
+ return;
1042
1584
  }
1043
- if (key in instance.provides) {
1044
- return instance.provides[key];
1585
+ if (isShowElement(element)) {
1586
+ const when = readReactive3(props.when);
1587
+ await streamHydratableNodeToController(when ? resolveShowChildren3(element, when) : props.fallback, controller, encoder, ctx, isComponentRoot);
1588
+ return;
1045
1589
  }
1046
- if (arguments.length >= 2) {
1047
- return defaultValue;
1590
+ if (isForElement(element)) {
1591
+ await streamHydratableForElement(element, controller, encoder, ctx);
1592
+ return;
1048
1593
  }
1049
- console.warn(`[Sinwan] inject() key "${String(key)}" not found and no default provided.`);
1050
- return;
1051
- }
1052
- // src/renderer/attributes.ts
1053
- var SKIP_PROPS = new Set(["children", "key", "ref", "dangerouslySetInnerHTML"]);
1054
- var DOM_PROPERTIES = new Set(["value", "checked", "selected", "disabled", "readOnly", "multiple", "indeterminate"]);
1055
- var PROP_ALIASES = {
1056
- className: "class",
1057
- htmlFor: "for",
1058
- tabIndex: "tabindex",
1059
- crossOrigin: "crossorigin"
1060
- };
1061
- function applyAttributes(el, props) {
1062
- const disposers = [];
1063
- for (const [key, value] of Object.entries(props)) {
1064
- if (SKIP_PROPS.has(key) || isEventProp(key))
1065
- continue;
1066
- if (isSignal(value) || isComputed(value)) {
1067
- const dispose = effect(() => {
1068
- setSingleAttribute(el, key, value.value);
1069
- });
1070
- disposers.push(dispose);
1071
- } else {
1072
- setSingleAttribute(el, key, value);
1073
- }
1594
+ if (typeof tag === "function") {
1595
+ await streamHydratableComponent(tag, props, controller, encoder, ctx, true);
1596
+ return;
1074
1597
  }
1075
- return disposers;
1076
- }
1077
- function setSingleAttribute(el, key, value) {
1078
- const attrName = PROP_ALIASES[key] ?? key;
1079
- if (attrName === "style" && typeof value === "object" && value !== null) {
1080
- applyStyle(el, value);
1598
+ if (typeof tag === "string") {
1599
+ await streamHydratableIntrinsic(tag, props, children, controller, encoder, ctx, isComponentRoot);
1081
1600
  return;
1082
1601
  }
1083
- if (attrName === "class" && typeof value === "object" && value !== null) {
1084
- applyClass(el, value);
1602
+ await streamHydratableNodeToController(children, controller, encoder, ctx);
1603
+ }
1604
+ async function streamHydratableComponent(component, props, controller, encoder, ctx, isComponentRoot) {
1605
+ const parentInstance = getCurrentInstance();
1606
+ const instance = createComponentInstance(component, props, parentInstance);
1607
+ if (parentInstance) {
1608
+ parentInstance.children.push(instance);
1609
+ }
1610
+ const prev = setCurrentInstance(instance);
1611
+ try {
1612
+ const result = await component(props);
1613
+ await streamHydratableNodeToController(result, controller, encoder, ctx, isComponentRoot);
1614
+ } finally {
1615
+ setCurrentInstance(prev);
1616
+ }
1617
+ }
1618
+ async function streamHydratableIntrinsic(tag, props, children, controller, encoder, ctx, isComponentRoot) {
1619
+ const attrs = renderHydratableAttributes(props, ctx, isComponentRoot);
1620
+ const dangerous = props.dangerouslySetInnerHTML;
1621
+ enqueue(controller, encoder, attrs ? `<${tag}${attrs}>` : `<${tag}>`);
1622
+ if (VOID_ELEMENTS4.has(tag)) {
1085
1623
  return;
1086
1624
  }
1087
- if (value == null || value === false) {
1088
- domOps.removeAttribute(el, attrName);
1089
- if (DOM_PROPERTIES.has(attrName)) {
1090
- domOps.setProperty(el, attrName, attrName === "value" ? "" : false);
1625
+ if (dangerous && typeof dangerous.__html === "string") {
1626
+ enqueue(controller, encoder, dangerous.__html);
1627
+ } else {
1628
+ for (const child of children) {
1629
+ await streamHydratableNodeToController(child, controller, encoder, ctx);
1091
1630
  }
1631
+ }
1632
+ enqueue(controller, encoder, `</${tag}>`);
1633
+ }
1634
+ async function streamHydratableForElement(element, controller, encoder, ctx) {
1635
+ const props = element.props;
1636
+ const each = readReactive3(props.each);
1637
+ if (!Array.isArray(each) || typeof props.children !== "function") {
1092
1638
  return;
1093
1639
  }
1094
- if (value === true) {
1095
- domOps.setAttribute(el, attrName, "");
1096
- if (DOM_PROPERTIES.has(attrName)) {
1097
- domOps.setProperty(el, attrName, true);
1640
+ for (let index = 0;index < each.length; index++) {
1641
+ await streamHydratableNodeToController(props.children(each[index], () => index), controller, encoder, ctx);
1642
+ }
1643
+ }
1644
+ function renderHydratableAttributes(props, ctx, isComponentRoot) {
1645
+ let attrs = "";
1646
+ if (isComponentRoot) {
1647
+ attrs += ` ${COMP_ID_ATTR}="${compId(ctx.componentIndex++)}"`;
1648
+ }
1649
+ const eventParts = [];
1650
+ for (const [key, value] of Object.entries(props)) {
1651
+ if (key === "children" || key === "key" || key === "ref" || key === "dangerouslySetInnerHTML") {
1652
+ continue;
1098
1653
  }
1099
- return;
1654
+ if (isEventProp(key)) {
1655
+ eventParts.push(`${toEventName(key)}:${ctx.eventIndex++}`);
1656
+ continue;
1657
+ }
1658
+ const resolvedValue = readReactive3(value);
1659
+ if (resolvedValue == null || resolvedValue === false)
1660
+ continue;
1661
+ const attrName = key === "className" ? "class" : key === "htmlFor" ? "for" : key;
1662
+ if (resolvedValue === true) {
1663
+ attrs += ` ${attrName}`;
1664
+ continue;
1665
+ }
1666
+ attrs += ` ${attrName}="${escapeHtml(String(resolvedValue))}"`;
1100
1667
  }
1101
- if (DOM_PROPERTIES.has(attrName)) {
1102
- domOps.setProperty(el, attrName, value);
1668
+ if (eventParts.length > 0) {
1669
+ attrs += ` ${EVENT_ATTR}="${eventParts.join(",")}"`;
1670
+ }
1671
+ return attrs;
1672
+ }
1673
+ function enqueue(controller, encoder, html) {
1674
+ controller.enqueue(encoder.encode(html));
1675
+ }
1676
+ async function streamForElement(element, controller, encoder) {
1677
+ const props = element.props;
1678
+ const each = readReactive3(props.each);
1679
+ if (!Array.isArray(each) || typeof props.children !== "function") {
1103
1680
  return;
1104
1681
  }
1105
- domOps.setAttribute(el, attrName, String(value));
1682
+ for (let index = 0;index < each.length; index++) {
1683
+ await streamNode(props.children(each[index], () => index), controller, encoder);
1684
+ }
1106
1685
  }
1107
- function applyStyle(el, styles) {
1108
- for (const [prop, val] of Object.entries(styles)) {
1109
- if (prop.includes("-")) {
1110
- el.style.setProperty(prop, val);
1111
- } else {
1112
- el.style[prop] = val;
1113
- }
1686
+ function resolveShowChildren3(element, value) {
1687
+ const children = element.props.children ?? element.children;
1688
+ if (typeof children === "function") {
1689
+ return children(value);
1114
1690
  }
1691
+ return children;
1115
1692
  }
1116
- function applyClass(el, value) {
1117
- let classStr;
1118
- if (Array.isArray(value)) {
1119
- classStr = value.filter(Boolean).join(" ");
1120
- } else if (typeof value === "object" && value !== null) {
1121
- classStr = Object.entries(value).filter(([, v]) => Boolean(v)).map(([k]) => k).join(" ");
1122
- } else {
1123
- classStr = String(value);
1693
+ function readReactive3(value) {
1694
+ return isSignal(value) || isComputed(value) ? value.value : value;
1695
+ }
1696
+ // src/server/hydration-markers.ts
1697
+ function createHydrationContext() {
1698
+ return { componentIndex: 0, textIndex: 0, eventIndex: 0 };
1699
+ }
1700
+ async function renderToHydratableString(component, props) {
1701
+ const ctx = createHydrationContext();
1702
+ const mergedProps = props ?? {};
1703
+ const instance = createComponentInstance(component, mergedProps, null);
1704
+ const prev = setCurrentInstance(instance);
1705
+ try {
1706
+ const result = await component(mergedProps);
1707
+ if (result && typeof result === "object" && "tag" in result) {
1708
+ return renderElementH(result, ctx, true);
1709
+ }
1710
+ return renderNodeH(result, ctx);
1711
+ } finally {
1712
+ setCurrentInstance(prev);
1124
1713
  }
1125
- domOps.setAttribute(el, "class", classStr);
1126
1714
  }
1127
-
1128
- // src/renderer/render-element.ts
1715
+ async function renderNodeToHydratableString(node) {
1716
+ const ctx = createHydrationContext();
1717
+ return renderNodeH(node, ctx);
1718
+ }
1129
1719
  var VOID_ELEMENTS5 = new Set([
1130
1720
  "area",
1131
1721
  "base",
@@ -1142,273 +1732,269 @@ var VOID_ELEMENTS5 = new Set([
1142
1732
  "track",
1143
1733
  "wbr"
1144
1734
  ]);
1145
- function renderElementToDOM(element, parent, anchor = null) {
1146
- const { tag, props, children } = element;
1147
- if (tag === "" || tag === Fragment) {
1148
- return renderFragmentToDOM(children, parent, anchor);
1735
+ function renderNodeH(node, ctx) {
1736
+ if (node == null || typeof node === "boolean")
1737
+ return "";
1738
+ if (typeof node === "string")
1739
+ return escapeHtml(node);
1740
+ if (typeof node === "number")
1741
+ return String(node);
1742
+ if (node instanceof HtmlEscapedString)
1743
+ return node.value;
1744
+ if (isSignal(node) || isComputed(node)) {
1745
+ const value = node.value;
1746
+ const idx = ctx.textIndex++;
1747
+ return `${textMarkerOpen(idx)}${escapeHtml(String(value))}${textMarkerCloseStr()}`;
1149
1748
  }
1150
- if (typeof tag === "function") {
1151
- return renderComponentToDOM(tag, props, parent, anchor);
1749
+ if (Array.isArray(node)) {
1750
+ return node.map((child) => renderNodeH(child, ctx)).join("");
1152
1751
  }
1153
- if (typeof tag === "string") {
1154
- return renderIntrinsicToDOM(tag, props, children, parent, anchor);
1752
+ if (node instanceof Promise) {
1753
+ return "";
1754
+ }
1755
+ if (typeof node === "object" && "tag" in node) {
1756
+ return renderElementH(node, ctx, false);
1155
1757
  }
1156
- return renderFragmentToDOM(children, parent, anchor);
1758
+ return escapeHtml(String(node));
1157
1759
  }
1158
- function renderIntrinsicToDOM(tag, props, children, parent, anchor) {
1159
- const el = domOps.createElement(tag);
1160
- const attrDisposers = applyAttributes(el, props);
1161
- const eventCleanups = bindEvents(el, props);
1162
- let mountedChildren = [];
1163
- if (!VOID_ELEMENTS5.has(tag)) {
1164
- const dangerous = props.dangerouslySetInnerHTML;
1165
- if (dangerous && typeof dangerous.__html === "string") {
1166
- el.innerHTML = dangerous.__html;
1167
- } else {
1168
- mountedChildren = renderChildrenToDOM(children, el);
1169
- }
1760
+ function renderElementH(element, ctx, isComponentRoot) {
1761
+ const { tag, props, children } = element;
1762
+ if (tag === "") {
1763
+ return children.map((child) => renderNodeH(child, ctx)).join("");
1170
1764
  }
1171
- if (anchor) {
1172
- domOps.insertBefore(parent, el, anchor);
1173
- } else {
1174
- domOps.appendChild(parent, el);
1765
+ if (tag === Show || tag === For) {
1766
+ return renderElementH(tag(props), ctx, isComponentRoot);
1175
1767
  }
1176
- return {
1177
- type: "element",
1178
- node: el,
1179
- children: mountedChildren,
1180
- eventCleanups,
1181
- attrDisposers
1182
- };
1768
+ if (isShowElement(element)) {
1769
+ const when = readReactive4(props.when);
1770
+ const content = when ? resolveShowChildren4(element, when) : props.fallback;
1771
+ return renderNodeMaybeRoot(content, ctx, isComponentRoot);
1772
+ }
1773
+ if (isForElement(element)) {
1774
+ return renderForElementH(element, ctx);
1775
+ }
1776
+ if (typeof tag === "function") {
1777
+ return renderComponentH(tag, props, ctx);
1778
+ }
1779
+ if (typeof tag === "string") {
1780
+ return renderIntrinsicH(tag, props, children, ctx, isComponentRoot);
1781
+ }
1782
+ return children.map((child) => renderNodeH(child, ctx)).join("");
1183
1783
  }
1184
- function renderComponentToDOM(component, props, parent, anchor) {
1784
+ function renderComponentH(component, props, ctx) {
1185
1785
  const parentInstance = getCurrentInstance();
1186
1786
  const instance = createComponentInstance(component, props, parentInstance);
1187
1787
  if (parentInstance) {
1188
1788
  parentInstance.children.push(instance);
1189
1789
  }
1190
- const prevInstance = setCurrentInstance(instance);
1191
- let result;
1192
- let child;
1790
+ const prev = setCurrentInstance(instance);
1193
1791
  try {
1194
- result = component(props);
1792
+ const result = component(props);
1195
1793
  if (result && typeof result === "object" && "tag" in result) {
1196
- child = renderElementToDOM(result, parent, anchor);
1197
- } else {
1198
- child = renderNodeToDOM(result, parent, anchor);
1199
- }
1200
- } catch (err) {
1201
- setCurrentInstance(prevInstance);
1202
- handleComponentError(instance, err);
1203
- const text = domOps.createTextNode("");
1204
- if (anchor) {
1205
- domOps.insertBefore(parent, text, anchor);
1206
- } else {
1207
- domOps.appendChild(parent, text);
1794
+ return renderElementH(result, ctx, true);
1208
1795
  }
1209
- return {
1210
- type: "component",
1211
- children: [{ type: "text", node: text }],
1212
- disposers: [],
1213
- instance
1214
- };
1215
- }
1216
- setCurrentInstance(prevInstance);
1217
- instance.element = child;
1218
- return {
1219
- type: "component",
1220
- children: [child],
1221
- disposers: instance.effects,
1222
- instance
1223
- };
1224
- }
1225
- function renderFragmentToDOM(children, parent, anchor) {
1226
- const anchorComment = domOps.createComment("Sinwan-f");
1227
- if (anchor) {
1228
- domOps.insertBefore(parent, anchorComment, anchor);
1229
- } else {
1230
- domOps.appendChild(parent, anchorComment);
1231
- }
1232
- const mounted = [];
1233
- for (const child of children) {
1234
- mounted.push(renderNodeToDOM(child, parent, anchor));
1796
+ return renderNodeH(result, ctx);
1797
+ } finally {
1798
+ setCurrentInstance(prev);
1235
1799
  }
1236
- return { type: "fragment", children: mounted, anchor: anchorComment };
1237
1800
  }
1238
-
1239
- // src/renderer/render-children.ts
1240
- function renderNodeToDOM(node, parent, anchor = null) {
1241
- if (node == null || typeof node === "boolean") {
1242
- const text2 = domOps.createTextNode("");
1243
- insertNode(parent, text2, anchor);
1244
- return { type: "text", node: text2 };
1245
- }
1246
- if (typeof node === "string") {
1247
- const text2 = domOps.createTextNode(node);
1248
- insertNode(parent, text2, anchor);
1249
- return { type: "text", node: text2 };
1250
- }
1251
- if (typeof node === "number") {
1252
- const text2 = domOps.createTextNode(String(node));
1253
- insertNode(parent, text2, anchor);
1254
- return { type: "text", node: text2 };
1255
- }
1256
- if (node instanceof HtmlEscapedString) {
1257
- const text2 = domOps.createTextNode(node.value);
1258
- insertNode(parent, text2, anchor);
1259
- return { type: "text", node: text2 };
1260
- }
1261
- if (isSignal(node) || isComputed(node)) {
1262
- const text2 = domOps.createTextNode(String(node.value));
1263
- insertNode(parent, text2, anchor);
1264
- const dispose = effect(() => {
1265
- domOps.setTextContent(text2, String(node.value));
1266
- });
1267
- return { type: "reactive-text", node: text2, dispose };
1801
+ function renderIntrinsicH(tag, props, children, ctx, isComponentRoot) {
1802
+ let attrs = "";
1803
+ if (isComponentRoot) {
1804
+ attrs += ` ${COMP_ID_ATTR}="${compId(ctx.componentIndex++)}"`;
1268
1805
  }
1269
- if (Array.isArray(node)) {
1270
- return renderArrayToDOM(node, parent, anchor);
1806
+ const eventParts = [];
1807
+ for (const [key, value] of Object.entries(props)) {
1808
+ if (key === "children" || key === "key" || key === "ref" || key === "dangerouslySetInnerHTML") {
1809
+ continue;
1810
+ }
1811
+ if (isEventProp(key)) {
1812
+ const eventName = toEventName(key);
1813
+ eventParts.push(`${eventName}:${ctx.eventIndex++}`);
1814
+ continue;
1815
+ }
1816
+ if (value == null || value === false)
1817
+ continue;
1818
+ let resolvedValue = value;
1819
+ if (isSignal(value) || isComputed(value)) {
1820
+ resolvedValue = value.value;
1821
+ }
1822
+ if (resolvedValue === true) {
1823
+ const attrName2 = key === "className" ? "class" : key === "htmlFor" ? "for" : key;
1824
+ attrs += ` ${attrName2}`;
1825
+ continue;
1826
+ }
1827
+ const attrName = key === "className" ? "class" : key === "htmlFor" ? "for" : key;
1828
+ attrs += ` ${attrName}="${escapeHtml(String(resolvedValue))}"`;
1271
1829
  }
1272
- if (node instanceof Promise) {
1273
- const placeholder = domOps.createTextNode("");
1274
- insertNode(parent, placeholder, anchor);
1275
- node.then((resolved) => {
1276
- const mounted = renderNodeToDOM(resolved, parent, placeholder);
1277
- domOps.remove(placeholder);
1278
- });
1279
- return { type: "text", node: placeholder };
1830
+ if (eventParts.length > 0) {
1831
+ attrs += ` ${EVENT_ATTR}="${eventParts.join(",")}"`;
1280
1832
  }
1281
- if (typeof node === "object" && "tag" in node) {
1282
- return renderElementToDOM(node, parent, anchor);
1833
+ if (VOID_ELEMENTS5.has(tag)) {
1834
+ return `<${tag}${attrs}>`;
1283
1835
  }
1284
- const text = domOps.createTextNode(String(node));
1285
- insertNode(parent, text, anchor);
1286
- return { type: "text", node: text };
1836
+ const dangerous = props.dangerouslySetInnerHTML;
1837
+ if (dangerous && typeof dangerous.__html === "string") {
1838
+ return `<${tag}${attrs}>${dangerous.__html}</${tag}>`;
1839
+ }
1840
+ const childrenHtml = children.map((child) => renderNodeH(child, ctx)).join("");
1841
+ return `<${tag}${attrs}>${childrenHtml}</${tag}>`;
1287
1842
  }
1288
- function renderArrayToDOM(nodes, parent, anchor) {
1289
- const anchorComment = domOps.createComment("Sinwan-f");
1290
- insertNode(parent, anchorComment, anchor);
1291
- const children = [];
1292
- for (const child of nodes) {
1293
- children.push(renderNodeToDOM(child, parent, anchor));
1843
+ function renderNodeMaybeRoot(node, ctx, isComponentRoot) {
1844
+ if (isComponentRoot && node && typeof node === "object" && !Array.isArray(node) && "tag" in node) {
1845
+ return renderElementH(node, ctx, true);
1294
1846
  }
1295
- return { type: "fragment", children, anchor: anchorComment };
1847
+ return renderNodeH(node, ctx);
1296
1848
  }
1297
- function renderChildrenToDOM(children, parent) {
1298
- const mounted = [];
1299
- for (const child of children) {
1300
- mounted.push(renderNodeToDOM(child, parent));
1849
+ function renderForElementH(element, ctx) {
1850
+ const props = element.props;
1851
+ const each = readReactive4(props.each);
1852
+ if (!Array.isArray(each) || typeof props.children !== "function") {
1853
+ return "";
1301
1854
  }
1302
- return mounted;
1855
+ return each.map((item, index) => renderNodeH(props.children(item, () => index), ctx)).join("");
1303
1856
  }
1304
- function insertNode(parent, child, anchor) {
1305
- if (anchor) {
1306
- domOps.insertBefore(parent, child, anchor);
1307
- } else {
1308
- domOps.appendChild(parent, child);
1857
+ function resolveShowChildren4(element, value) {
1858
+ const children = element.props.children ?? element.children;
1859
+ if (typeof children === "function") {
1860
+ return children(value);
1309
1861
  }
1862
+ return children;
1310
1863
  }
1311
-
1312
- // src/renderer/mount.ts
1313
- function mount(component, container, props) {
1314
- container.innerHTML = "";
1315
- const mergedProps = props ?? {};
1316
- const instance = createComponentInstance(component, mergedProps, null);
1317
- let result;
1318
- let root;
1319
- setCurrentInstance(instance);
1864
+ function readReactive4(value) {
1865
+ return isSignal(value) || isComputed(value) ? value.value : value;
1866
+ }
1867
+ // src/index.ts
1868
+ var exports_src = {};
1869
+ __export(exports_src, {
1870
+ unmountNode: () => unmountNode,
1871
+ streamPage: () => streamPage,
1872
+ streamHydratablePage: () => streamHydratablePage,
1873
+ streamHydratableNode: () => streamHydratableNode,
1874
+ signal: () => signal,
1875
+ setDOMOps: () => setDOMOps,
1876
+ safeHtml: () => safeHtml,
1877
+ resetDOMOps: () => resetDOMOps,
1878
+ renderToString: () => renderToString,
1879
+ renderToHydratableString: () => renderToHydratableString,
1880
+ renderPage: () => renderPage,
1881
+ renderNodeToHydratableString: () => renderNodeToHydratableString,
1882
+ renderNodeToDOM: () => renderNodeToDOM,
1883
+ renderElementToDOM: () => renderElementToDOM,
1884
+ render: () => render,
1885
+ registerPage: () => registerPage,
1886
+ raw: () => raw,
1887
+ provide: () => provide,
1888
+ onUpdated: () => onUpdated,
1889
+ onUnmounted: () => onUnmounted,
1890
+ onMounted: () => onMounted,
1891
+ onError: () => onError,
1892
+ nextTick: () => nextTick,
1893
+ mount: () => mount,
1894
+ jsxs: () => jsxs,
1895
+ jsxDEV: () => jsxDEV,
1896
+ jsx: () => jsx,
1897
+ isSignal: () => isSignal,
1898
+ isSafeHtml: () => isSafeHtml,
1899
+ isComputed: () => isComputed,
1900
+ inject: () => inject,
1901
+ hydrate: () => hydrate,
1902
+ hasPage: () => hasPage,
1903
+ getPage: () => getPage,
1904
+ getCurrentInstance: () => getCurrentInstance,
1905
+ escapeHtml: () => escapeHtml,
1906
+ effect: () => effect,
1907
+ domOps: () => domOps,
1908
+ createPage: () => createPage,
1909
+ createLayout: () => createLayout,
1910
+ createComponent: () => createComponent,
1911
+ computed: () => computed,
1912
+ batch: () => batch,
1913
+ Show: () => Show,
1914
+ HtmlEscapedString: () => HtmlEscapedString,
1915
+ Fragment: () => Fragment,
1916
+ For: () => For
1917
+ });
1918
+ module.exports = __toCommonJS(exports_src);
1919
+ // src/reactivity/batch.ts
1920
+ var batchDepth = 0;
1921
+ function batch(fn) {
1922
+ batchDepth++;
1320
1923
  try {
1321
- result = component(mergedProps);
1322
- if (result instanceof Promise) {
1323
- const placeholder = document.createTextNode("");
1324
- container.appendChild(placeholder);
1325
- root = { type: "text", node: placeholder };
1326
- result.then((resolved) => {
1327
- container.innerHTML = "";
1328
- setCurrentInstance(instance);
1329
- root = renderElementToDOM(resolved, container);
1330
- setCurrentInstance(null);
1331
- instance.element = root;
1332
- fireMountedHooks(instance);
1333
- });
1334
- } else if (result && typeof result === "object" && "tag" in result) {
1335
- root = renderElementToDOM(result, container);
1336
- } else {
1337
- root = renderNodeToDOM(result, container);
1924
+ fn();
1925
+ } finally {
1926
+ batchDepth--;
1927
+ if (batchDepth === 0) {
1928
+ flushSync();
1338
1929
  }
1339
- } catch (err) {
1340
- setCurrentInstance(null);
1341
- handleComponentError(instance, err);
1342
- return {
1343
- root: { type: "text", node: document.createTextNode("") },
1344
- unmount() {}
1345
- };
1346
1930
  }
1347
- setCurrentInstance(null);
1348
- instance.element = root;
1349
- fireMountedHooks(instance);
1350
- return {
1351
- root,
1352
- unmount() {
1353
- fireUnmountedHooks(instance);
1354
- unmountNode(root);
1355
- container.innerHTML = "";
1356
- }
1357
- };
1358
1931
  }
1359
- function render(node, container) {
1360
- container.innerHTML = "";
1361
- const root = renderNodeToDOM(node, container);
1362
- return {
1363
- root,
1364
- unmount() {
1365
- unmountNode(root);
1366
- container.innerHTML = "";
1367
- }
1368
- };
1932
+ // src/component/lifecycle.ts
1933
+ function onMounted(fn) {
1934
+ const instance = getCurrentInstance();
1935
+ if (!instance) {
1936
+ throw new Error("onMounted() called outside of component setup.");
1937
+ }
1938
+ instance._mountedHooks.push(() => withInstance(instance, fn));
1369
1939
  }
1370
- function unmountNode(node) {
1371
- switch (node.type) {
1372
- case "text":
1373
- break;
1374
- case "reactive-text":
1375
- node.dispose();
1376
- break;
1377
- case "element":
1378
- for (const dispose of node.attrDisposers) {
1379
- dispose();
1380
- }
1381
- for (const cleanup of node.eventCleanups) {
1382
- cleanup();
1383
- }
1384
- for (const child of node.children) {
1385
- unmountNode(child);
1386
- }
1387
- break;
1388
- case "fragment":
1389
- for (const child of node.children) {
1390
- unmountNode(child);
1391
- }
1392
- break;
1393
- case "reactive-block":
1394
- node.dispose();
1395
- for (const child of node.children) {
1396
- unmountNode(child);
1397
- }
1398
- break;
1399
- case "component":
1400
- if (node.instance) {
1401
- fireUnmountedHooks(node.instance);
1402
- } else {
1403
- for (const dispose of node.disposers) {
1404
- dispose();
1405
- }
1406
- }
1407
- for (const child of node.children) {
1408
- unmountNode(child);
1409
- }
1410
- break;
1940
+ function onUnmounted(fn) {
1941
+ const instance = getCurrentInstance();
1942
+ if (!instance) {
1943
+ throw new Error("onUnmounted() called outside of component setup.");
1944
+ }
1945
+ instance._unmountedHooks.push(() => withInstance(instance, fn));
1946
+ }
1947
+ function onUpdated(fn) {
1948
+ const instance = getCurrentInstance();
1949
+ if (!instance) {
1950
+ throw new Error("onUpdated() called outside of component setup.");
1951
+ }
1952
+ instance._updatedHooks.push(() => withInstance(instance, fn));
1953
+ }
1954
+ function onError(fn) {
1955
+ const instance = getCurrentInstance();
1956
+ if (!instance) {
1957
+ throw new Error("onError() called outside of component setup.");
1958
+ }
1959
+ instance._errorHooks.push((err) => withInstance(instance, () => fn(err)));
1960
+ }
1961
+ // src/component/create.ts
1962
+ function createComponent(fn) {
1963
+ const component = (props) => fn(props);
1964
+ component._SinwanComponent = true;
1965
+ component._displayName = fn.name || "AnonymousComponent";
1966
+ return component;
1967
+ }
1968
+ function createPage(fn) {
1969
+ const page = (data) => fn(data);
1970
+ page._SinwanPage = true;
1971
+ page._displayName = fn.name || "AnonymousPage";
1972
+ return page;
1973
+ }
1974
+ function createLayout(fn) {
1975
+ return createComponent(fn);
1976
+ }
1977
+ // src/component/provide-inject.ts
1978
+ function provide(key, value) {
1979
+ const instance = getCurrentInstance();
1980
+ if (!instance) {
1981
+ throw new Error("provide() called outside of component setup.");
1982
+ }
1983
+ instance.provides[key] = value;
1984
+ }
1985
+ function inject(key, defaultValue) {
1986
+ const instance = getCurrentInstance();
1987
+ if (!instance) {
1988
+ throw new Error("inject() called outside of component setup.");
1989
+ }
1990
+ if (key in instance.provides) {
1991
+ return instance.provides[key];
1992
+ }
1993
+ if (arguments.length >= 2) {
1994
+ return defaultValue;
1411
1995
  }
1996
+ console.warn(`[Sinwan] inject() key "${String(key)}" not found and no default provided.`);
1997
+ return;
1412
1998
  }
1413
1999
  // src/hydration/walk.ts
1414
2000
  function advance(cursor) {
@@ -1449,6 +2035,7 @@ function hydrateNode(node, cursor) {
1449
2035
  }
1450
2036
  function hydrateReactiveText(reactive, cursor) {
1451
2037
  const openComment = cursor.current;
2038
+ const owner = getCurrentInstance();
1452
2039
  if (openComment && openComment.nodeType === 8 && parseTextOpenMarker(openComment) >= 0) {
1453
2040
  advance(cursor);
1454
2041
  const textNode2 = advance(cursor);
@@ -1456,21 +2043,36 @@ function hydrateReactiveText(reactive, cursor) {
1456
2043
  if (closeComment && closeComment.nodeType === 8 && isTextCloseMarker(closeComment)) {
1457
2044
  advance(cursor);
1458
2045
  }
2046
+ let initialized2 = false;
1459
2047
  const dispose2 = effect(() => {
1460
2048
  textNode2.data = String(reactive.value);
2049
+ if (initialized2) {
2050
+ queueUpdatedHooks(owner);
2051
+ }
2052
+ initialized2 = true;
1461
2053
  });
1462
2054
  return { type: "reactive-text", node: textNode2, dispose: dispose2 };
1463
2055
  }
1464
2056
  const textNode = advance(cursor);
1465
2057
  if (textNode) {
2058
+ let initialized2 = false;
1466
2059
  const dispose2 = effect(() => {
1467
2060
  textNode.data = String(reactive.value);
2061
+ if (initialized2) {
2062
+ queueUpdatedHooks(owner);
2063
+ }
2064
+ initialized2 = true;
1468
2065
  });
1469
2066
  return { type: "reactive-text", node: textNode, dispose: dispose2 };
1470
2067
  }
1471
2068
  const newText = document.createTextNode(String(reactive.value));
2069
+ let initialized = false;
1472
2070
  const dispose = effect(() => {
1473
2071
  newText.data = String(reactive.value);
2072
+ if (initialized) {
2073
+ queueUpdatedHooks(owner);
2074
+ }
2075
+ initialized = true;
1474
2076
  });
1475
2077
  return { type: "reactive-text", node: newText, dispose };
1476
2078
  }
@@ -1479,6 +2081,12 @@ function hydrateElement(element, cursor) {
1479
2081
  if (tag === "") {
1480
2082
  return hydrateArray(children, cursor);
1481
2083
  }
2084
+ if (tag === Show || tag === For) {
2085
+ return hydrateElement(tag(props), cursor);
2086
+ }
2087
+ if (isShowElement(element) || isForElement(element)) {
2088
+ return hydrateControlFlow(element, cursor);
2089
+ }
1482
2090
  if (typeof tag === "function") {
1483
2091
  return hydrateComponent(tag, props, cursor);
1484
2092
  }
@@ -1497,6 +2105,7 @@ function hydrateIntrinsic(tag, props, children, cursor) {
1497
2105
  el.removeAttribute("data-sinwan-ev");
1498
2106
  const attrDisposers = hydrateAttributes(el, props);
1499
2107
  const eventCleanups = bindEvents(el, props);
2108
+ const refCleanup = applyRef2(el, props.ref);
1500
2109
  const childCursor = {
1501
2110
  parent: el,
1502
2111
  current: el.firstChild
@@ -1510,15 +2119,18 @@ function hydrateIntrinsic(tag, props, children, cursor) {
1510
2119
  node: el,
1511
2120
  children: mountedChildren,
1512
2121
  eventCleanups,
1513
- attrDisposers
2122
+ attrDisposers,
2123
+ refCleanup
1514
2124
  };
1515
2125
  }
1516
2126
  function hydrateAttributes(el, props) {
1517
2127
  const disposers = [];
2128
+ const owner = getCurrentInstance();
1518
2129
  for (const [key, value] of Object.entries(props)) {
1519
2130
  if (key === "children" || key === "key" || key === "ref" || isEventProp(key))
1520
2131
  continue;
1521
2132
  if (isSignal(value) || isComputed(value)) {
2133
+ let initialized = false;
1522
2134
  const dispose = effect(() => {
1523
2135
  const v = value.value;
1524
2136
  const attrName = key === "className" ? "class" : key === "htmlFor" ? "for" : key;
@@ -1529,12 +2141,63 @@ function hydrateAttributes(el, props) {
1529
2141
  } else {
1530
2142
  el.setAttribute(attrName, String(v));
1531
2143
  }
2144
+ if (initialized) {
2145
+ queueUpdatedHooks(owner);
2146
+ }
2147
+ initialized = true;
1532
2148
  });
1533
2149
  disposers.push(dispose);
1534
2150
  }
1535
2151
  }
1536
2152
  return disposers;
1537
2153
  }
2154
+ function hydrateControlFlow(element, cursor) {
2155
+ if (isShowElement(element)) {
2156
+ const when = readReactive5(element.props.when);
2157
+ const content = when ? resolveShowChildren5(element, when) : element.props.fallback;
2158
+ return hydrateContent(content, cursor);
2159
+ }
2160
+ if (isForElement(element)) {
2161
+ const props = element.props;
2162
+ const items = readReactive5(props.each);
2163
+ const children = Array.isArray(items) && typeof props.children === "function" ? items.map((item, index) => props.children(item, () => index)) : [];
2164
+ return hydrateArray(children, cursor);
2165
+ }
2166
+ return hydrateArray(element.children, cursor);
2167
+ }
2168
+ function hydrateContent(content, cursor) {
2169
+ if (content == null || typeof content === "boolean") {
2170
+ return hydrateArray([], cursor);
2171
+ }
2172
+ return Array.isArray(content) ? hydrateArray(content, cursor) : hydrateNode(content, cursor);
2173
+ }
2174
+ function resolveShowChildren5(element, value) {
2175
+ const children = element.props.children ?? element.children;
2176
+ if (typeof children === "function") {
2177
+ return children(value);
2178
+ }
2179
+ return children;
2180
+ }
2181
+ function readReactive5(value) {
2182
+ return isSignal(value) || isComputed(value) ? value.value : value;
2183
+ }
2184
+ function applyRef2(el, ref) {
2185
+ const value = ref;
2186
+ if (!value) {
2187
+ return null;
2188
+ }
2189
+ if (typeof value === "function") {
2190
+ value(el);
2191
+ return () => value(null);
2192
+ }
2193
+ if (typeof value === "object" && "current" in value) {
2194
+ value.current = el;
2195
+ return () => {
2196
+ value.current = null;
2197
+ };
2198
+ }
2199
+ return null;
2200
+ }
1538
2201
  function hydrateComponent(component, props, cursor) {
1539
2202
  const parentInstance = getCurrentInstance();
1540
2203
  const instance = createComponentInstance(component, props, parentInstance);
@@ -1621,5 +2284,5 @@ function hydrate(component, container, props) {
1621
2284
  };
1622
2285
  }
1623
2286
 
1624
- //# debugId=F8718FCED5F52B5764756E2164756E21
2287
+ //# debugId=82414CC0A24D81DC64756E2164756E21
1625
2288
  //# sourceMappingURL=index.development.js.map