semajsx 0.5.2 → 0.6.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 (65) hide show
  1. package/dist/client-CButR91p.mjs +740 -0
  2. package/dist/client-CButR91p.mjs.map +1 -0
  3. package/dist/dom/index.d.mts +2 -2
  4. package/dist/dom/jsx-dev-runtime.d.mts +3 -3
  5. package/dist/dom/jsx-runtime.d.mts +3 -3
  6. package/dist/{helpers-CfRDJgcP.d.mts → helpers-C8GKdDrJ.d.mts} +3 -3
  7. package/dist/{helpers-CfRDJgcP.d.mts.map → helpers-C8GKdDrJ.d.mts.map} +1 -1
  8. package/dist/{index-Ch9GwToI.d.mts → index-D_FIlSk3.d.mts} +3 -3
  9. package/dist/{index-Ch9GwToI.d.mts.map → index-D_FIlSk3.d.mts.map} +1 -1
  10. package/dist/{index-B1pjI-Su.d.mts → index-PYr1aNIz.d.mts} +2 -2
  11. package/dist/{index-B1pjI-Su.d.mts.map → index-PYr1aNIz.d.mts.map} +1 -1
  12. package/dist/index.d.mts +6 -6
  13. package/dist/{island-marker-BJIO07Vj.d.mts → island-marker-Dne5tuWe.d.mts} +1 -1
  14. package/dist/island-marker-Dne5tuWe.d.mts.map +1 -0
  15. package/dist/{jsx-fNlLjLou.d.mts → jsx-CFnuxPMI.d.mts} +2 -2
  16. package/dist/{jsx-fNlLjLou.d.mts.map → jsx-CFnuxPMI.d.mts.map} +1 -1
  17. package/dist/{jsx-runtime-BFuFPDzn.d.mts → jsx-runtime-Dc77fsnM.d.mts} +3 -3
  18. package/dist/{jsx-runtime-BFuFPDzn.d.mts.map → jsx-runtime-Dc77fsnM.d.mts.map} +1 -1
  19. package/dist/{jsx-runtime-BBi9E0Hz.d.mts → jsx-runtime-tIuFmhTh.d.mts} +4 -4
  20. package/dist/{jsx-runtime-BBi9E0Hz.d.mts.map → jsx-runtime-tIuFmhTh.d.mts.map} +1 -1
  21. package/dist/{lucide-CVtHepGM.mjs → lucide-C5BghhSl.mjs} +1 -1
  22. package/dist/{lucide-CVtHepGM.mjs.map → lucide-C5BghhSl.mjs.map} +1 -1
  23. package/dist/{resource-BQI6AeJ0.d.mts → resource-CNwiNxJX.d.mts} +2 -2
  24. package/dist/{resource-BQI6AeJ0.d.mts.map → resource-CNwiNxJX.d.mts.map} +1 -1
  25. package/dist/signal/index.d.mts +2 -2
  26. package/dist/{signal-BwxUlXKs.d.mts → signal-BcaF-fWG.d.mts} +1 -1
  27. package/dist/{signal-BwxUlXKs.d.mts.map → signal-BcaF-fWG.d.mts.map} +1 -1
  28. package/dist/{src-L88LbwEv.mjs → src-75qcxwT_.mjs} +2 -2
  29. package/dist/{src-L88LbwEv.mjs.map → src-75qcxwT_.mjs.map} +1 -1
  30. package/dist/{src-DuSN6go_.mjs → src-B4VBiHa8.mjs} +116 -4
  31. package/dist/src-B4VBiHa8.mjs.map +1 -0
  32. package/dist/ssg/index.d.mts +2 -2
  33. package/dist/ssg/index.mjs +2 -2
  34. package/dist/ssg/plugins/docs-theme.d.mts +7 -4
  35. package/dist/ssg/plugins/docs-theme.d.mts.map +1 -1
  36. package/dist/ssg/plugins/docs-theme.mjs +170 -46
  37. package/dist/ssg/plugins/docs-theme.mjs.map +1 -1
  38. package/dist/ssg/plugins/lucide.d.mts +2 -2
  39. package/dist/ssg/plugins/lucide.mjs +1 -1
  40. package/dist/ssr/client.d.mts +7 -6
  41. package/dist/ssr/client.d.mts.map +1 -1
  42. package/dist/ssr/client.mjs +4 -682
  43. package/dist/ssr/index.d.mts +2 -2
  44. package/dist/ssr/index.d.mts.map +1 -1
  45. package/dist/ssr/index.mjs +1 -1
  46. package/dist/style/index.d.mts +2 -2
  47. package/dist/style/react.d.mts +2 -2
  48. package/dist/style/vue.d.mts +2 -2
  49. package/dist/terminal/index.d.mts +4 -4
  50. package/dist/terminal/jsx-dev-runtime.d.mts +4 -4
  51. package/dist/terminal/jsx-runtime.d.mts +4 -4
  52. package/dist/{types-D0jRO840.d.mts → types-Bj5q5x2Q.d.mts} +1 -1
  53. package/dist/{types-D0jRO840.d.mts.map → types-Bj5q5x2Q.d.mts.map} +1 -1
  54. package/dist/{types-C9fiRu6l.d.mts → types-BmDIxXiP.d.mts} +2 -2
  55. package/dist/{types-C9fiRu6l.d.mts.map → types-BmDIxXiP.d.mts.map} +1 -1
  56. package/dist/{types-CZMcXQTW.d.mts → types-C83YtOen.d.mts} +2 -2
  57. package/dist/{types-CZMcXQTW.d.mts.map → types-C83YtOen.d.mts.map} +1 -1
  58. package/dist/{types-BlaUrkq0.d.mts → types-CVPg8ByY.d.mts} +2 -2
  59. package/dist/{types-BlaUrkq0.d.mts.map → types-CVPg8ByY.d.mts.map} +1 -1
  60. package/dist/{types-DucvOZQ2.d.mts → types-ii0bAipe.d.mts} +2 -2
  61. package/dist/{types-DucvOZQ2.d.mts.map → types-ii0bAipe.d.mts.map} +1 -1
  62. package/package.json +1 -1
  63. package/dist/island-marker-BJIO07Vj.d.mts.map +0 -1
  64. package/dist/src-DuSN6go_.mjs.map +0 -1
  65. package/dist/ssr/client.mjs.map +0 -1
@@ -1,685 +1,7 @@
1
- import { t as isSignal } from "../utils-DbTAs943.mjs";
2
- import { v as Fragment } from "../src-DW3tIczg.mjs";
3
- import { d as setProperty, u as render } from "../src-BqX3sryB.mjs";
1
+ import "../src-DW3tIczg.mjs";
2
+ import "../src-BqX3sryB.mjs";
4
3
  import "../src-Ds9vl42d.mjs";
5
4
  import { a as isLinkVNode, c as getIslandMetadata, d as island, i as isAssetVNode, l as isIslandComponent, n as LINK_MARKER, o as isStyleVNode, r as STYLE_MARKER, s as resource, t as ASSET_MARKER, u as isIslandVNode } from "../resource-DSlXDZZi.mjs";
5
+ import { a as resolveCSS, c as getIslandInfo, d as hydrateAllIslands, f as hydrateIsland, h as markIslandHydrated, i as resolveAsset, l as hasIslands, m as hydrateIslands, n as getManifest, o as setManifest, p as hydrateIslandById, r as loadStylesheet, s as getIslandIds, t as clientResource, u as hydrate } from "../client-CButR91p.mjs";
6
6
 
7
- //#region ../ssr/src/client/hydrate.ts
8
- /**
9
- * Type guard for async iterators
10
- */
11
- function isAsyncIterator(value) {
12
- if (!value || typeof value !== "object") return false;
13
- const obj = value;
14
- return typeof obj[Symbol.asyncIterator] === "function" || typeof obj.next === "function" && typeof obj.return === "function";
15
- }
16
- /**
17
- * Hydrate a server-rendered DOM tree with client-side interactivity
18
- * Unlike render(), this preserves existing DOM and only attaches event listeners
19
- *
20
- * @param vnode - The VNode to hydrate
21
- * @param container - The DOM container with server-rendered content
22
- * @returns The hydrated root node
23
- *
24
- * @example
25
- * ```tsx
26
- * const vnode = <Counter initial={5} />
27
- * const container = document.querySelector('[data-island-id="island-0"]')
28
- * hydrate(vnode, container)
29
- * ```
30
- */
31
- function hydrate(vnode, container) {
32
- const nodeToHydrate = container.firstChild;
33
- if (!nodeToHydrate) {
34
- console.warn("[Hydrate] Container is empty, falling back to render");
35
- const rendered = renderNode(vnode, container);
36
- if (rendered) container.appendChild(rendered);
37
- return rendered;
38
- }
39
- try {
40
- hydrateNode(vnode, nodeToHydrate, container);
41
- return nodeToHydrate;
42
- } catch (error) {
43
- console.error("[Hydrate] Error during hydration:", error);
44
- console.warn("[Hydrate] Falling back to client-side rendering");
45
- container.innerHTML = "";
46
- return renderNode(vnode, container);
47
- }
48
- }
49
- /**
50
- * Hydrate a VNode onto an existing DOM node
51
- */
52
- function hydrateNode(vnode, domNode, parentElement) {
53
- if (vnode == null) return;
54
- if (isSignal(vnode)) {
55
- hydrateSignalNode(vnode, domNode, parentElement);
56
- return;
57
- }
58
- if (typeof vnode === "string" || typeof vnode === "number") {
59
- if (domNode.nodeType === Node.TEXT_NODE) {
60
- const expectedText = String(vnode);
61
- if (domNode.textContent !== expectedText) {
62
- console.warn("[Hydrate] Text mismatch, updating:", domNode.textContent, "->", expectedText);
63
- domNode.textContent = expectedText;
64
- }
65
- }
66
- return;
67
- }
68
- if (Array.isArray(vnode)) {
69
- let currentDomNode = domNode;
70
- for (const child of vnode) if (currentDomNode) {
71
- hydrateNode(child, currentDomNode, parentElement);
72
- currentDomNode = currentDomNode.nextSibling;
73
- }
74
- return;
75
- }
76
- if (typeof vnode !== "object" || !("type" in vnode)) return;
77
- const vnodeTyped = vnode;
78
- if (vnodeTyped.type === "#signal") {
79
- const signal = vnodeTyped.props?.signal;
80
- if (signal && isSignal(signal)) hydrateSignalNode(signal, domNode, parentElement);
81
- return;
82
- }
83
- if (vnodeTyped.type === Fragment) {
84
- let currentDomNode = domNode;
85
- for (const child of vnodeTyped.children) if (currentDomNode) {
86
- hydrateNode(child, currentDomNode, parentElement);
87
- currentDomNode = currentDomNode.nextSibling;
88
- }
89
- return;
90
- }
91
- if (typeof vnodeTyped.type === "function") {
92
- let result = vnodeTyped.type(vnodeTyped.props || {});
93
- if (result instanceof Promise) {
94
- result.then((resolved) => hydrateNode(resolved, domNode, parentElement));
95
- return;
96
- }
97
- if (isAsyncIterator(result)) {
98
- result.next().then(({ value }) => {
99
- hydrateNode(value, domNode, parentElement);
100
- });
101
- return;
102
- }
103
- hydrateNode(result, domNode, parentElement);
104
- return;
105
- }
106
- if (typeof vnodeTyped.type === "string") {
107
- if (domNode.nodeType === Node.TEXT_NODE) return;
108
- if (domNode.nodeType !== Node.ELEMENT_NODE) {
109
- console.warn("[Hydrate] Expected element, got:", domNode.nodeType);
110
- return;
111
- }
112
- const element = domNode;
113
- if (element.tagName.toLowerCase() !== vnodeTyped.type.toLowerCase()) {
114
- console.warn("[Hydrate] Tag mismatch:", element.tagName, "vs", vnodeTyped.type);
115
- return;
116
- }
117
- hydrateProperties(element, vnodeTyped.props || {});
118
- hydrateChildren(element, vnodeTyped.children);
119
- return;
120
- }
121
- }
122
- /**
123
- * Hydrate properties onto an element
124
- * This is where we attach event listeners and set up reactive properties
125
- */
126
- function hydrateProperties(element, props) {
127
- for (const [key, value] of Object.entries(props)) {
128
- if (key === "children" || key === "key" || key === "ref") continue;
129
- if (key.startsWith("on")) {
130
- const eventName = key.slice(2).toLowerCase();
131
- if (typeof value === "function") element.addEventListener(eventName, value);
132
- continue;
133
- }
134
- if (isSignal(value)) {
135
- setProperty(element, key, value.value);
136
- value.subscribe((newValue) => {
137
- setProperty(element, key, newValue);
138
- });
139
- continue;
140
- }
141
- }
142
- if (props.ref) {
143
- if (typeof props.ref === "function") props.ref(element);
144
- else if (typeof props.ref === "object" && props.ref !== null) props.ref.current = element;
145
- }
146
- }
147
- /**
148
- * Hydrate children elements
149
- */
150
- function hydrateChildren(element, children) {
151
- let currentDomNode = element.firstChild;
152
- for (const child of children) {
153
- if (!currentDomNode) {
154
- console.warn("[Hydrate] Missing DOM node for child, appending");
155
- const newNode = renderNode(child, element);
156
- if (newNode) element.appendChild(newNode);
157
- continue;
158
- }
159
- hydrateNode(child, currentDomNode, element);
160
- currentDomNode = currentDomNode.nextSibling;
161
- }
162
- }
163
- /**
164
- * Hydrate a signal VNode
165
- * Set up reactivity to replace content when signal changes
166
- */
167
- function hydrateSignalNode(signal, domNode, parentElement) {
168
- const currentValue = signal.value;
169
- if (currentValue == null || currentValue === false || Array.isArray(currentValue) && currentValue.length === 0) if (domNode.nodeType === Node.COMMENT_NODE) {} else console.warn("[Hydrate] Expected comment marker for empty signal, got:", domNode.nodeType);
170
- else if (typeof currentValue === "string" || typeof currentValue === "number") {
171
- if (domNode.nodeType === Node.TEXT_NODE) {
172
- const expectedText = String(currentValue);
173
- if (domNode.textContent !== expectedText) {
174
- console.warn("[Hydrate] Signal text mismatch:", domNode.textContent, "->", expectedText);
175
- domNode.textContent = expectedText;
176
- }
177
- }
178
- } else hydrateNode(currentValue, domNode, parentElement);
179
- let anchor;
180
- let currentNodes = [];
181
- if (domNode.nodeType === Node.COMMENT_NODE) anchor = domNode;
182
- else {
183
- anchor = document.createComment("signal-anchor");
184
- if (domNode.parentNode) domNode.parentNode.insertBefore(anchor, domNode);
185
- currentNodes = [domNode];
186
- }
187
- signal.subscribe((newValue) => {
188
- const parent = anchor.parentNode;
189
- if (!parent) return;
190
- for (const node of currentNodes) if (node.parentNode) node.parentNode.removeChild(node);
191
- currentNodes = [];
192
- const newNode = renderNode(newValue, parentElement);
193
- if (newNode) if (newNode.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
194
- const fragment = newNode;
195
- const children = Array.from(fragment.childNodes);
196
- let insertAfter = anchor;
197
- for (const child of children) {
198
- parent.insertBefore(child, insertAfter.nextSibling);
199
- insertAfter = child;
200
- currentNodes.push(child);
201
- }
202
- } else {
203
- parent.insertBefore(newNode, anchor.nextSibling);
204
- currentNodes = [newNode];
205
- }
206
- });
207
- }
208
- /**
209
- * Render a VNode to a DOM node (fallback when hydration fails)
210
- * This is a simplified version of render() just for hydration fallback
211
- */
212
- function renderNode(vnode, parentElement) {
213
- if (vnode == null || vnode === false || vnode === true) return document.createComment("empty");
214
- if (isSignal(vnode)) return renderNode(vnode.value, parentElement);
215
- if (typeof vnode === "string" || typeof vnode === "number") return document.createTextNode(String(vnode));
216
- if (Array.isArray(vnode)) {
217
- if (vnode.length === 0) return document.createComment("empty");
218
- const fragment = document.createDocumentFragment();
219
- for (const child of vnode) {
220
- const node = renderNode(child, parentElement);
221
- if (node) fragment.appendChild(node);
222
- }
223
- return fragment;
224
- }
225
- if (typeof vnode === "object" && "type" in vnode) {
226
- const vnodeTyped = vnode;
227
- if (vnodeTyped.type === "#text") return document.createTextNode(String(vnodeTyped.props?.nodeValue || ""));
228
- if (vnodeTyped.type === "#signal") {
229
- const signal = vnodeTyped.props?.signal;
230
- if (signal && isSignal(signal)) return renderNode(signal.value, parentElement);
231
- return document.createTextNode("");
232
- }
233
- if (vnodeTyped.type === Fragment) {
234
- const fragment = document.createDocumentFragment();
235
- for (const child of vnodeTyped.children) {
236
- const node = renderNode(child, parentElement);
237
- if (node) fragment.appendChild(node);
238
- }
239
- return fragment;
240
- }
241
- if (typeof vnodeTyped.type === "function") return renderNode(vnodeTyped.type(vnodeTyped.props || {}), parentElement);
242
- if (typeof vnodeTyped.type === "string") {
243
- const element = document.createElement(vnodeTyped.type);
244
- const props = vnodeTyped.props || {};
245
- for (const [key, value] of Object.entries(props)) {
246
- if (key === "children" || key === "key") continue;
247
- setProperty(element, key, value);
248
- }
249
- for (const child of vnodeTyped.children) {
250
- const childNode = renderNode(child, element);
251
- if (childNode) element.appendChild(childNode);
252
- }
253
- return element;
254
- }
255
- }
256
- return null;
257
- }
258
- /**
259
- * Hydrate an island by ID
260
- * Handles both single-element islands (with data-island-id) and fragment islands (with comment markers)
261
- *
262
- * @param islandId - The island ID to hydrate
263
- * @param Component - The component function to render
264
- * @param markHydrated - Callback to mark the island as hydrated
265
- *
266
- * @example
267
- * ```tsx
268
- * import { hydrateIsland, markIslandHydrated } from '@semajsx/ssr/client';
269
- * import Counter from './Counter';
270
- *
271
- * hydrateIsland('counter-0', Counter, markIslandHydrated);
272
- * ```
273
- */
274
- function hydrateIsland(islandId, Component, markHydrated) {
275
- const element = document.querySelector(`[data-island-id="${islandId}"]`);
276
- if (element) {
277
- const props = JSON.parse(element.getAttribute("data-island-props") || "{}");
278
- const parent = element.parentNode;
279
- if (!parent) return;
280
- const vnode = {
281
- type: Component,
282
- props,
283
- children: []
284
- };
285
- const temp = document.createElement("div");
286
- render(vnode, temp);
287
- const children = Array.from(temp.childNodes);
288
- for (const child of children) parent.insertBefore(child, element);
289
- parent.removeChild(element);
290
- markHydrated(islandId);
291
- return;
292
- }
293
- const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_COMMENT);
294
- let startComment = null;
295
- let comment;
296
- while (comment = walker.nextNode()) if (comment.textContent === `island:${islandId}`) {
297
- startComment = comment;
298
- break;
299
- }
300
- if (startComment) {
301
- const script = document.querySelector(`script[data-island="${islandId}"]`);
302
- const props = script ? JSON.parse(script.textContent || "{}") : {};
303
- const nodesToRemove = [];
304
- let sibling = startComment.nextSibling;
305
- let endComment = null;
306
- while (sibling) {
307
- if (sibling.nodeType === Node.COMMENT_NODE && sibling.textContent === `/island:${islandId}`) {
308
- endComment = sibling;
309
- break;
310
- }
311
- nodesToRemove.push(sibling);
312
- sibling = sibling.nextSibling;
313
- }
314
- for (const node of nodesToRemove) node.parentNode?.removeChild(node);
315
- const vnode = {
316
- type: Component,
317
- props,
318
- children: []
319
- };
320
- const parent = startComment.parentNode;
321
- if (parent) {
322
- const temp = document.createElement("div");
323
- render(vnode, temp);
324
- const children = Array.from(temp.childNodes);
325
- for (const child of children) parent.insertBefore(child, endComment);
326
- }
327
- startComment.parentNode?.removeChild(startComment);
328
- if (endComment) endComment.parentNode?.removeChild(endComment);
329
- if (script) script.parentNode?.removeChild(script);
330
- markHydrated(islandId);
331
- }
332
- }
333
- /**
334
- * Find all islands on the page (both element and fragment types)
335
- */
336
- function findAllIslands() {
337
- const islands = [];
338
- const elements = document.querySelectorAll("[data-island-id]");
339
- for (const el of elements) {
340
- const id = el.getAttribute("data-island-id");
341
- const propsStr = el.getAttribute("data-island-props");
342
- if (id) islands.push({
343
- id,
344
- props: propsStr ? JSON.parse(propsStr) : {},
345
- element: el
346
- });
347
- }
348
- const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_COMMENT);
349
- let comment;
350
- while (comment = walker.nextNode()) {
351
- const match = comment.textContent?.match(/^island:(.+)$/);
352
- if (match && match[1]) {
353
- const id = match[1];
354
- let endComment = null;
355
- let sibling = comment.nextSibling;
356
- while (sibling) {
357
- if (sibling.nodeType === Node.COMMENT_NODE && sibling.textContent === `/island:${id}`) {
358
- endComment = sibling;
359
- break;
360
- }
361
- sibling = sibling.nextSibling;
362
- }
363
- const script = document.querySelector(`script[type="application/json"][data-island="${id}"]`);
364
- const props = script ? JSON.parse(script.textContent || "{}") : {};
365
- islands.push({
366
- id,
367
- props,
368
- startComment: comment,
369
- endComment: endComment || void 0
370
- });
371
- }
372
- }
373
- return islands;
374
- }
375
- /**
376
- * Hydrate all islands on the page
377
- * This function is typically called once after the page loads
378
- *
379
- * @example
380
- * ```tsx
381
- * // In your client entry point
382
- * import { hydrateIslands } from '@semajsx/ssr/client'
383
- *
384
- * // Wait for DOM to be ready
385
- * if (document.readyState === 'loading') {
386
- * document.addEventListener('DOMContentLoaded', hydrateIslands)
387
- * } else {
388
- * hydrateIslands()
389
- * }
390
- * ```
391
- */
392
- async function hydrateIslands() {
393
- const islands = findAllIslands();
394
- if (islands.length === 0) return;
395
- console.log(`[SemaJSX] Found ${islands.length} islands to hydrate`);
396
- const hydrations = islands.map((island) => waitForIslandScript(island));
397
- await Promise.all(hydrations);
398
- console.log(`[SemaJSX] All islands hydrated`);
399
- }
400
- /**
401
- * Wait for an island's script to load and hydrate it
402
- * The actual hydration is performed by the island's entry point script
403
- * This function just waits for it to complete
404
- */
405
- async function waitForIslandScript(island) {
406
- const { id: islandId, element, startComment } = island;
407
- if (element?.hasAttribute("data-hydrated")) return;
408
- if (startComment?.parentElement?.querySelector(`[data-island-hydrated="${islandId}"]`)) return;
409
- return new Promise((resolve) => {
410
- const maxAttempts = 200;
411
- let attempts = 0;
412
- const checkInterval = setInterval(() => {
413
- if (element ? element.hasAttribute("data-hydrated") : document.querySelector(`[data-island-hydrated="${islandId}"]`) !== null) {
414
- clearInterval(checkInterval);
415
- resolve();
416
- } else if (++attempts >= maxAttempts) {
417
- clearInterval(checkInterval);
418
- console.warn(`[SemaJSX] Island ${islandId} hydration timeout`);
419
- resolve();
420
- }
421
- }, 50);
422
- });
423
- }
424
- /**
425
- * Get island info by ID
426
- */
427
- function getIslandInfo(islandId) {
428
- const element = document.querySelector(`[data-island-id="${islandId}"]`);
429
- if (element) {
430
- const propsStr = element.getAttribute("data-island-props");
431
- return {
432
- id: islandId,
433
- props: propsStr ? JSON.parse(propsStr) : {},
434
- element
435
- };
436
- }
437
- const script = document.querySelector(`script[type="application/json"][data-island="${islandId}"]`);
438
- if (script) {
439
- const props = JSON.parse(script.textContent || "{}");
440
- const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_COMMENT);
441
- let comment;
442
- while (comment = walker.nextNode()) if (comment.textContent === `island:${islandId}`) return {
443
- id: islandId,
444
- props,
445
- startComment: comment
446
- };
447
- }
448
- return null;
449
- }
450
- /**
451
- * Manual hydration for a specific island
452
- * Useful for lazy-loading islands on interaction
453
- *
454
- * @param islandId - The island ID to hydrate
455
- *
456
- * @example
457
- * ```tsx
458
- * // Lazy load an island on click
459
- * button.addEventListener('click', () => {
460
- * hydrateIslandById('island-0')
461
- * })
462
- * ```
463
- */
464
- async function hydrateIslandById(islandId) {
465
- const island = getIslandInfo(islandId);
466
- if (!island) {
467
- console.error(`[SemaJSX] Island not found: ${islandId}`);
468
- return;
469
- }
470
- await waitForIslandScript(island);
471
- }
472
- /**
473
- * Check if islands are present on the page
474
- */
475
- function hasIslands() {
476
- if (document.querySelectorAll("[data-island-id]").length > 0) return true;
477
- return document.querySelectorAll("script[type=\"application/json\"][data-island]").length > 0;
478
- }
479
- /**
480
- * Get all island IDs on the page
481
- */
482
- function getIslandIds() {
483
- const ids = [];
484
- const elements = document.querySelectorAll("[data-island-id]");
485
- for (const el of elements) {
486
- const id = el.getAttribute("data-island-id");
487
- if (id) ids.push(id);
488
- }
489
- const scripts = document.querySelectorAll("script[type=\"application/json\"][data-island]");
490
- for (const script of scripts) {
491
- const id = script.getAttribute("data-island");
492
- if (id) ids.push(id);
493
- }
494
- return ids;
495
- }
496
- /**
497
- * Mark an island as hydrated
498
- * This should be called by the island entry point after hydration completes
499
- */
500
- function markIslandHydrated(islandId) {
501
- const element = document.querySelector(`[data-island-id="${islandId}"]`);
502
- if (element) {
503
- element.setAttribute("data-hydrated", "true");
504
- return;
505
- }
506
- const script = document.querySelector(`script[type="application/json"][data-island="${islandId}"]`);
507
- if (script) {
508
- script.setAttribute("data-island-hydrated", islandId);
509
- script.remove();
510
- }
511
- }
512
- /**
513
- * Hydrate all islands with a given component source
514
- * Finds all elements with data-island-src and hydrates them
515
- *
516
- * @param componentSrc - The component source key (e.g., "components/Counter")
517
- * @param Component - The component function to render
518
- *
519
- * @example
520
- * ```tsx
521
- * import { hydrateAllIslands } from '@semajsx/ssr/client';
522
- * import Counter from './Counter';
523
- *
524
- * hydrateAllIslands('components/Counter', Counter);
525
- * ```
526
- */
527
- function hydrateAllIslands(componentSrc, Component) {
528
- const elements = document.querySelectorAll(`[data-island-src="${componentSrc}"]`);
529
- const scripts = document.querySelectorAll(`script[type="application/json"][data-island-src="${componentSrc}"]`);
530
- elements.forEach((element) => {
531
- const islandId = element.getAttribute("data-island-id");
532
- if (!islandId) return;
533
- if (element.hasAttribute("data-hydrated")) return;
534
- const props = JSON.parse(element.getAttribute("data-island-props") || "{}");
535
- const parent = element.parentNode;
536
- if (!parent) return;
537
- const vnode = {
538
- type: Component,
539
- props,
540
- children: []
541
- };
542
- const temp = document.createElement("div");
543
- render(vnode, temp);
544
- const children = Array.from(temp.childNodes);
545
- for (const child of children) {
546
- parent.insertBefore(child, element);
547
- if (child instanceof Element && child === children[0]) {
548
- child.setAttribute("data-island-id", islandId);
549
- child.setAttribute("data-hydrated", "true");
550
- }
551
- }
552
- element.remove();
553
- });
554
- scripts.forEach((script) => {
555
- const islandId = script.getAttribute("data-island");
556
- if (!islandId) return;
557
- if (script.hasAttribute("data-island-hydrated")) return;
558
- const props = JSON.parse(script.textContent || "{}");
559
- const startMarker = `island:${islandId}`;
560
- const endMarker = `/island:${islandId}`;
561
- const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_COMMENT, null);
562
- let startNode = null;
563
- let endNode = null;
564
- let node;
565
- while (node = walker.nextNode()) if (node.nodeValue === startMarker) startNode = node;
566
- else if (node.nodeValue === endMarker) {
567
- endNode = node;
568
- break;
569
- }
570
- if (startNode && endNode && startNode.parentNode) {
571
- const nodes = [];
572
- let current = startNode.nextSibling;
573
- while (current && current !== endNode) {
574
- nodes.push(current);
575
- current = current.nextSibling;
576
- }
577
- nodes.forEach((n) => {
578
- if (n.parentNode) n.parentNode.removeChild(n);
579
- });
580
- const vnode = {
581
- type: Component,
582
- props,
583
- children: []
584
- };
585
- const temp = document.createElement("div");
586
- render(vnode, temp);
587
- const newChildren = Array.from(temp.childNodes);
588
- for (const child of newChildren) startNode.parentNode.insertBefore(child, endNode);
589
- script.setAttribute("data-island-hydrated", islandId);
590
- script.remove();
591
- }
592
- });
593
- }
594
-
595
- //#endregion
596
- //#region ../ssr/src/client/client-resource.ts
597
- let _manifest = null;
598
- const loadedStyles = /* @__PURE__ */ new Set();
599
- /**
600
- * Set the client manifest (called during initialization)
601
- */
602
- function setManifest(manifest) {
603
- _manifest = manifest;
604
- }
605
- /**
606
- * Get the current manifest
607
- */
608
- function getManifest() {
609
- return _manifest;
610
- }
611
- /**
612
- * Resolve a CSS path using the manifest
613
- */
614
- function resolveCSS(href) {
615
- if (!_manifest) return href;
616
- const lookupPath = href.startsWith("/") ? href.slice(1) : href;
617
- return _manifest.css[lookupPath] || href;
618
- }
619
- /**
620
- * Resolve an asset path using the manifest
621
- */
622
- function resolveAsset(src) {
623
- if (!_manifest) return src;
624
- const lookupPath = src.startsWith("/") ? src.slice(1) : src;
625
- return _manifest.assets[lookupPath] || src;
626
- }
627
- /**
628
- * Dynamically load a stylesheet
629
- */
630
- function loadStylesheet(href) {
631
- const resolvedHref = resolveCSS(href);
632
- if (loadedStyles.has(resolvedHref)) return Promise.resolve();
633
- return new Promise((resolve, reject) => {
634
- if (document.querySelector(`link[href="${resolvedHref}"]`)) {
635
- loadedStyles.add(resolvedHref);
636
- resolve();
637
- return;
638
- }
639
- const link = document.createElement("link");
640
- link.rel = "stylesheet";
641
- link.href = resolvedHref;
642
- link.onload = () => {
643
- loadedStyles.add(resolvedHref);
644
- resolve();
645
- };
646
- link.onerror = () => {
647
- reject(/* @__PURE__ */ new Error(`Failed to load stylesheet: ${resolvedHref}`));
648
- };
649
- document.head.appendChild(link);
650
- });
651
- }
652
- /**
653
- * Create client-side resource tools
654
- *
655
- * @example
656
- * ```tsx
657
- * import { clientResource } from '@semajsx/ssr/client';
658
- *
659
- * const { Style, url } = clientResource();
660
- *
661
- * export default function Counter() {
662
- * return (
663
- * <>
664
- * <Style href="./counter.css" />
665
- * <img src={url('./icon.png')} />
666
- * </>
667
- * );
668
- * }
669
- * ```
670
- */
671
- function clientResource() {
672
- return {
673
- Style({ href }) {
674
- if (typeof document !== "undefined") loadStylesheet(href);
675
- return null;
676
- },
677
- url(path) {
678
- return resolveAsset(path);
679
- }
680
- };
681
- }
682
-
683
- //#endregion
684
- export { ASSET_MARKER, LINK_MARKER, STYLE_MARKER, clientResource, getIslandIds, getIslandInfo, getIslandMetadata, getManifest, hasIslands, hydrate, hydrateAllIslands, hydrateIsland, hydrateIslandById, hydrateIslands, isAssetVNode, isIslandComponent, isIslandVNode, isLinkVNode, isStyleVNode, island, loadStylesheet, markIslandHydrated, resolveAsset, resolveCSS, resource, setManifest };
685
- //# sourceMappingURL=client.mjs.map
7
+ export { ASSET_MARKER, LINK_MARKER, STYLE_MARKER, clientResource, getIslandIds, getIslandInfo, getIslandMetadata, getManifest, hasIslands, hydrate, hydrateAllIslands, hydrateIsland, hydrateIslandById, hydrateIslands, isAssetVNode, isIslandComponent, isIslandVNode, isLinkVNode, isStyleVNode, island, loadStylesheet, markIslandHydrated, resolveAsset, resolveCSS, resource, setManifest };
@@ -1,5 +1,5 @@
1
- import { d as VNode } from "../types-CZMcXQTW.mjs";
2
- import { A as RouterConfig, C as IslandScriptTransformer, D as RouteContext, E as RenderToStringOptions, O as RouteHandler, S as IslandScriptContext, T as RenderResult, _ as BuildScriptEntry, a as ResourceTools, d as resource, f as App, g as BuildResult, h as BuildOptions, i as LinkProps, j as SSRResult, k as RouteMeta, m as AssetMetadata, n as AssetProps, o as STYLE_MARKER, p as AppConfig, r as LINK_MARKER, s as StyleProps, t as ASSET_MARKER, v as DevOptions, w as LinkMetadata, x as IslandMetadata, y as DocumentTemplate } from "../resource-BQI6AeJ0.mjs";
1
+ import { d as VNode } from "../types-C83YtOen.mjs";
2
+ import { A as RouterConfig, C as IslandScriptTransformer, D as RouteContext, E as RenderToStringOptions, O as RouteHandler, S as IslandScriptContext, T as RenderResult, _ as BuildScriptEntry, a as ResourceTools, d as resource, f as App, g as BuildResult, h as BuildOptions, i as LinkProps, j as SSRResult, k as RouteMeta, m as AssetMetadata, n as AssetProps, o as STYLE_MARKER, p as AppConfig, r as LINK_MARKER, s as StyleProps, t as ASSET_MARKER, v as DevOptions, w as LinkMetadata, x as IslandMetadata, y as DocumentTemplate } from "../resource-CNwiNxJX.mjs";
3
3
  import { PluginOption, ViteDevServer } from "vite";
4
4
 
5
5
  //#region ../ssr/src/app.d.ts