UNPKG

@stacksjs/stx

Version:

A performant UI Framework. Powered by Bun.

91 lines (90 loc) 3.03 kB
// src/client.ts var isBrowser = typeof window !== "undefined" && typeof document !== "undefined"; async function hydrateIsland(element, name, handler) { if (!isBrowser) return; try { const id = element.getAttribute("data-island-id"); const propsScript = document.querySelector(`script[data-island-props="${id}"]`); const props = propsScript ? JSON.parse(propsScript.textContent || "{}") : {}; const module = await handler(); const hydrateFn = module.default || module.hydrate; if (typeof hydrateFn !== "function") { console.error(`Island handler for "${name}" does not export a valid hydration function`); return; } await hydrateFn(element, props); element.setAttribute("data-hydrated", "true"); } catch (error) { console.error(`Failed to hydrate island "${name}":`, error); } } function hydrateIslands(handlers) { if (!isBrowser) return; const islands = document.querySelectorAll("[data-island]"); const lazyIslands = []; islands.forEach((island) => { const name = island.getAttribute("data-island"); const priority = island.getAttribute("data-priority") || "lazy"; if (!name || !handlers[name] || island.getAttribute("data-hydrated") === "true") { return; } if (priority === "eager") { hydrateIsland(island, name, handlers[name]); } else { lazyIslands.push([island, name]); } }); if (lazyIslands.length > 0 && "IntersectionObserver" in window) { const observer = new IntersectionObserver((entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { const element = entry.target; const name = element.getAttribute("data-island"); if (name && handlers[name] && element.getAttribute("data-hydrated") !== "true") { hydrateIsland(element, name, handlers[name]); observer.unobserve(element); } } }); }, { rootMargin: "200px", threshold: 0 }); lazyIslands.forEach(([element]) => { observer.observe(element); }); } else { setTimeout(() => { lazyIslands.forEach(([element, name]) => { if (element.getAttribute("data-hydrated") !== "true") { hydrateIsland(element, name, handlers[name]); } }); }, 1000); } } function preloadIslandHandlers(handlers) { if (!isBrowser) return; Object.entries(handlers).forEach(([name, getHandler]) => { try { const handlerString = getHandler.toString(); const moduleMatch = handlerString.match(/import\(['"](.+)['"]\)/); if (moduleMatch && moduleMatch[1]) { const link = document.createElement("link"); link.rel = "modulepreload"; link.href = moduleMatch[1]; link.setAttribute("data-island-module", name); document.head.appendChild(link); } } catch (error) { console.warn(`Failed to preload island handler for "${name}":`, error); } }); } export { preloadIslandHandlers, hydrateIslands };