@sigx/server-renderer 0.1.3 → 0.1.5
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.
- package/dist/client/hydrate.d.ts +19 -0
- package/dist/client/hydrate.d.ts.map +1 -0
- package/dist/client/index.d.ts +10 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +661 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/registry.d.ts +46 -0
- package/dist/client/registry.d.ts.map +1 -0
- package/dist/client/types.d.ts +43 -0
- package/dist/client/types.d.ts.map +1 -0
- package/dist/client-directives.d.ts +96 -0
- package/dist/client-directives.d.ts.map +1 -0
- package/dist/index.d.ts +42 -19
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1097 -50
- package/dist/index.js.map +1 -1
- package/dist/server/context.d.ts +118 -0
- package/dist/server/context.d.ts.map +1 -0
- package/dist/server/index.d.ts +11 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +574 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/stream.d.ts +34 -0
- package/dist/server/stream.d.ts.map +1 -0
- package/package.json +21 -6
- package/src/jsx.d.ts +62 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["isComponent","_pendingServerState: Record<string, any> | null","_cachedIslandData: Record<string, IslandInfo> | null","childDom: Node | null","endNode: Node | null","current: Node | null","targetElement: Element | null","anchor: Comment | null","componentId: number | null","mountHooks: ((ctx: any) => void)[]","cleanupHooks: ((ctx: any) => void)[]","componentCtx: ComponentSetupContext","renderFn: (() => any) | undefined","endDom: Node | null","filtered: Record<string, any>","islandData: Record<string, IslandInfo>","node: Comment | null","node: Node | null","container: Node | null","vnode: VNode"],"sources":["../../src/client/registry.ts","../../src/client/hydrate.ts"],"sourcesContent":["/**\r\n * Component Registry for Island Hydration\r\n * \r\n * Components must be registered before they can be hydrated as islands.\r\n */\r\n\r\nimport type { ComponentFactory } from './types.js';\r\n\r\n/**\r\n * Registry mapping component names to component factories\r\n */\r\nconst componentRegistry = new Map<string, ComponentFactory>();\r\n\r\n/**\r\n * Register a component for island hydration.\r\n * Components must be registered before hydrateIslands() is called.\r\n * \r\n * @example\r\n * ```ts\r\n * import { registerComponent } from '@sigx/server-renderer/client';\r\n * import { Counter } from './components/Counter';\r\n * \r\n * registerComponent('Counter', Counter);\r\n * ```\r\n */\r\nexport function registerComponent(name: string, component: ComponentFactory): void {\r\n componentRegistry.set(name, component);\r\n}\r\n\r\n/**\r\n * Register multiple components at once\r\n * \r\n * @example\r\n * ```ts\r\n * import { registerComponents } from '@sigx/server-renderer/client';\r\n * import * as Components from './components';\r\n * \r\n * registerComponents(Components);\r\n * ```\r\n */\r\nexport function registerComponents(components: Record<string, ComponentFactory>): void {\r\n for (const [name, component] of Object.entries(components)) {\r\n if (isComponent(component)) {\r\n registerComponent(name, component);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Get a registered component by name\r\n */\r\nexport function getComponent(name: string): ComponentFactory | undefined {\r\n return componentRegistry.get(name);\r\n}\r\n\r\n/**\r\n * Check if a value is a component factory\r\n */\r\nfunction isComponent(value: unknown): value is ComponentFactory {\r\n return typeof value === 'function' && '__setup' in value;\r\n}\r\n\r\n/**\r\n * Hydration Registry class for more advanced use cases\r\n */\r\nexport class HydrationRegistry {\r\n private components = new Map<string, ComponentFactory>();\r\n\r\n register(name: string, component: ComponentFactory): this {\r\n this.components.set(name, component);\r\n return this;\r\n }\r\n\r\n registerAll(components: Record<string, ComponentFactory>): this {\r\n for (const [name, component] of Object.entries(components)) {\r\n if (isComponent(component)) {\r\n this.register(name, component);\r\n }\r\n }\r\n return this;\r\n }\r\n\r\n get(name: string): ComponentFactory | undefined {\r\n return this.components.get(name);\r\n }\r\n\r\n has(name: string): boolean {\r\n return this.components.has(name);\r\n }\r\n}\r\n","/**\r\n * Client-side hydration for SSR'd content\r\n * \r\n * Hydration attaches the app to existing server-rendered DOM,\r\n * then delegates all updates to the runtime-dom renderer.\r\n */\r\n\r\nimport {\r\n VNode,\r\n Fragment,\r\n Text,\r\n ComponentSetupContext,\r\n setCurrentInstance,\r\n getCurrentInstance,\r\n signal,\r\n SlotsObject,\r\n createPropsAccessor,\r\n createSlots,\r\n normalizeSubTree,\r\n registerContextExtension\r\n} from '@sigx/runtime-core';\r\nimport { effect } from '@sigx/reactivity';\r\nimport { render, patchProp } from '@sigx/runtime-dom';\r\nimport { getComponent, registerComponent } from './registry.js';\r\nimport type { ComponentFactory, IslandInfo, HydrationOptions } from './types.js';\r\n\r\n// Re-export types\r\nexport type { HydrationOptions } from './types.js';\r\n\r\n// Track server state for async components being mounted after streaming\r\nlet _pendingServerState: Record<string, any> | null = null;\r\n\r\n/**\r\n * Set server state that should be used for the next component mount.\r\n * Used internally when mounting async components after streaming.\r\n */\r\nfunction setPendingServerState(state: Record<string, any> | null): void {\r\n _pendingServerState = state;\r\n}\r\n\r\n/**\r\n * Register the SSR context extension for all components.\r\n * This provides the `ssr` object with a no-op `load()` for client-side rendering.\r\n * Also handles server state restoration for async streamed components.\r\n */\r\nregisterContextExtension((ctx: any) => {\r\n // Check if we have pending server state (from async streaming)\r\n const serverState = _pendingServerState;\r\n if (serverState) {\r\n ctx._serverState = serverState;\r\n _pendingServerState = null; // Clear after use\r\n\r\n // Override signal function to use restoring signal\r\n ctx.signal = createRestoringSignal(serverState);\r\n\r\n // ssr.load() should be a no-op since we have restored state\r\n ctx.ssr = {\r\n load: (_fn: () => Promise<void>) => {\r\n // Skip - using restored server state\r\n },\r\n isServer: false,\r\n isHydrating: true\r\n };\r\n } else if (ctx._serverState) {\r\n // Already has server state (from hydration)\r\n ctx.ssr = {\r\n load: (_fn: () => Promise<void>) => {\r\n // Skip - using restored server state\r\n },\r\n isServer: false,\r\n isHydrating: true\r\n };\r\n } else {\r\n // Default client-side ssr helper - runs async functions for client-side navigation\r\n ctx.ssr = {\r\n load: (fn: () => Promise<void>) => {\r\n // On client-side navigation (not hydration), execute the async function\r\n fn().catch(err => console.error('[SSR] load error:', err));\r\n },\r\n isServer: false,\r\n isHydrating: false\r\n };\r\n }\r\n});\r\n\r\ninterface InternalVNode extends VNode {\r\n _subTree?: VNode;\r\n _effect?: any;\r\n _componentProps?: any;\r\n _slots?: any;\r\n}\r\n\r\n/**\r\n * Creates a signal function that restores state from server-captured values.\r\n * Used during hydration of async components to avoid re-fetching data.\r\n */\r\nfunction createRestoringSignal(serverState: Record<string, any>) {\r\n let signalIndex = 0;\r\n\r\n return function restoringSignal<T extends object>(initial: T, name?: string): ReturnType<typeof signal<T>> {\r\n // Generate a stable key for this signal (must match server-side)\r\n const key = name ?? `$${signalIndex++}`;\r\n\r\n // Check if we have server state for this signal\r\n if (key in serverState) {\r\n console.log(`[Hydrate] Restoring signal \"${key}\" from server state:`, serverState[key]);\r\n return signal(serverState[key] as T);\r\n }\r\n\r\n // No server state, use initial value\r\n return signal(initial);\r\n };\r\n}\r\n\r\n/**\r\n * Hydrate a server-rendered app.\r\n * \r\n * This walks the existing DOM to attach event handlers, runs component\r\n * setup functions to establish reactivity, then uses runtime-dom for updates.\r\n */\r\nexport function hydrate(element: any, container: Element): void {\r\n const vnode = normalizeElement(element);\r\n if (!vnode) return;\r\n\r\n // Walk existing DOM, attach handlers, and mount components\r\n hydrateNode(vnode, container.firstChild, container);\r\n\r\n // Store vnode on container for potential future use\r\n (container as any)._vnode = vnode;\r\n}\r\n\r\n// Cache for parsed island data - can be invalidated when async components stream in\r\nlet _cachedIslandData: Record<string, IslandInfo> | null = null;\r\n\r\n/**\r\n * Invalidate the island data cache (called when async components stream in)\r\n */\r\nfunction invalidateIslandCache(): void {\r\n _cachedIslandData = null;\r\n}\r\n\r\n/**\r\n * Get island data from the __SIGX_ISLANDS__ script tag\r\n */\r\nfunction getIslandData(): Record<string, IslandInfo> {\r\n if (_cachedIslandData !== null) {\r\n return _cachedIslandData;\r\n }\r\n\r\n const dataScript = document.getElementById('__SIGX_ISLANDS__');\r\n if (!dataScript) {\r\n _cachedIslandData = {};\r\n return _cachedIslandData!;\r\n }\r\n\r\n try {\r\n _cachedIslandData = JSON.parse(dataScript.textContent || '{}');\r\n } catch {\r\n console.error('Failed to parse island data');\r\n _cachedIslandData = {};\r\n }\r\n\r\n return _cachedIslandData!;\r\n}\r\n\r\n/**\r\n * Get server state for a specific island/component by ID\r\n */\r\nfunction getIslandServerState(componentId: number): Record<string, any> | undefined {\r\n const islandData = getIslandData();\r\n const info = islandData[String(componentId)];\r\n return info?.state;\r\n}\r\n\r\n/**\r\n * Normalize any element to VNode\r\n */\r\nfunction normalizeElement(element: any): VNode | null {\r\n if (element == null || element === true || element === false) {\r\n return null;\r\n }\r\n\r\n if (typeof element === 'string' || typeof element === 'number') {\r\n return {\r\n type: Text,\r\n props: {},\r\n key: null,\r\n children: [],\r\n dom: null,\r\n text: element\r\n };\r\n }\r\n\r\n return element as VNode;\r\n}\r\n\r\n/**\r\n * Check if type is a component\r\n */\r\nfunction isComponent(type: any): type is ComponentFactory {\r\n return typeof type === 'function' && '__setup' in type;\r\n}\r\n\r\n/**\r\n * Get hydration strategy from vnode props\r\n */\r\nfunction getHydrationStrategy(props: Record<string, any>): { strategy: 'load' | 'idle' | 'visible' | 'media' | 'only'; media?: string } | null {\r\n if (props['client:load'] !== undefined) return { strategy: 'load' };\r\n if (props['client:idle'] !== undefined) return { strategy: 'idle' };\r\n if (props['client:visible'] !== undefined) return { strategy: 'visible' };\r\n if (props['client:only'] !== undefined) return { strategy: 'only' };\r\n if (props['client:media'] !== undefined) {\r\n return { strategy: 'media', media: props['client:media'] };\r\n }\r\n return null;\r\n}\r\n\r\n/**\r\n * Hydrate a VNode against existing DOM\r\n * This only attaches event handlers and refs - no DOM creation\r\n */\r\nfunction hydrateNode(vnode: VNode, dom: Node | null, parent: Node): Node | null {\r\n if (!vnode) return dom;\r\n\r\n // Skip text separator comments (<!--t-->) but NOT component markers\r\n while (dom && dom.nodeType === Node.COMMENT_NODE) {\r\n const commentText = (dom as Comment).data;\r\n // Component markers start with $c: or /$c: - don't skip those\r\n if (commentText.startsWith('$c:') || commentText.startsWith('/$c:') ||\r\n commentText.startsWith('$island:')) {\r\n break;\r\n }\r\n dom = dom.nextSibling;\r\n }\r\n\r\n if (vnode.type === Text) {\r\n if (dom && dom.nodeType === Node.TEXT_NODE) {\r\n vnode.dom = dom;\r\n return dom.nextSibling;\r\n }\r\n return dom;\r\n }\r\n\r\n if (vnode.type === Fragment) {\r\n let current = dom;\r\n for (const child of vnode.children) {\r\n current = hydrateNode(child, current, parent);\r\n }\r\n return current;\r\n }\r\n\r\n if (isComponent(vnode.type)) {\r\n // Check for client:* hydration strategy\r\n const strategy = vnode.props ? getHydrationStrategy(vnode.props) : null;\r\n\r\n if (strategy) {\r\n // Schedule hydration based on strategy\r\n return scheduleComponentHydration(vnode, dom, parent, strategy);\r\n }\r\n\r\n // No directive - hydrate immediately\r\n return hydrateComponent(vnode, dom, parent);\r\n }\r\n\r\n if (typeof vnode.type === 'string') {\r\n if (!dom || dom.nodeType !== Node.ELEMENT_NODE) {\r\n console.warn('[Hydrate] Expected element but got:', dom);\r\n return dom;\r\n }\r\n\r\n const el = dom as Element;\r\n vnode.dom = el;\r\n\r\n // Attach event handlers and props using patchProp from runtime-dom\r\n if (vnode.props) {\r\n for (const key in vnode.props) {\r\n if (key === 'children' || key === 'key') continue;\r\n if (key.startsWith('client:')) continue;\r\n\r\n // Use patchProp for consistent prop handling (events, refs, etc.)\r\n patchProp(el, key, null, vnode.props[key]);\r\n }\r\n }\r\n\r\n // Hydrate children\r\n let childDom: Node | null = el.firstChild;\r\n for (const child of vnode.children) {\r\n childDom = hydrateNode(child, childDom, el);\r\n }\r\n\r\n // Fix select value after children are hydrated\r\n if (vnode.type === 'select' && vnode.props) {\r\n fixSelectValue(el as HTMLElement, vnode.props);\r\n }\r\n\r\n return el.nextSibling;\r\n }\r\n\r\n return dom;\r\n}\r\n\r\n/**\r\n * Schedule component hydration based on strategy.\r\n * Returns the next DOM node after this component's content.\r\n */\r\nfunction scheduleComponentHydration(\r\n vnode: VNode,\r\n dom: Node | null,\r\n parent: Node,\r\n strategy: { strategy: 'load' | 'idle' | 'visible' | 'media' | 'only'; media?: string }\r\n): Node | null {\r\n // Find component boundaries in DOM (start/end markers)\r\n const { startNode, endNode } = findComponentBoundaries(dom);\r\n\r\n const doHydrate = () => {\r\n hydrateComponent(vnode, startNode, parent);\r\n };\r\n\r\n switch (strategy.strategy) {\r\n case 'load':\r\n // Hydrate immediately\r\n console.log('[Hydrate] client:load - hydrating immediately');\r\n doHydrate();\r\n break;\r\n\r\n case 'idle':\r\n // Hydrate during browser idle time\r\n console.log('[Hydrate] client:idle - scheduling for idle time');\r\n if ('requestIdleCallback' in window) {\r\n requestIdleCallback(() => {\r\n console.log('[Hydrate] client:idle - idle callback fired');\r\n doHydrate();\r\n });\r\n } else {\r\n setTimeout(() => {\r\n console.log('[Hydrate] client:idle - timeout fallback fired');\r\n doHydrate();\r\n }, 200);\r\n }\r\n break;\r\n\r\n case 'visible':\r\n // Hydrate when component scrolls into view\r\n console.log('[Hydrate] client:visible - observing visibility');\r\n observeComponentVisibility(startNode, endNode, doHydrate);\r\n break;\r\n\r\n case 'media':\r\n // Hydrate when media query matches\r\n if (strategy.media) {\r\n const mql = window.matchMedia(strategy.media);\r\n if (mql.matches) {\r\n doHydrate();\r\n } else {\r\n const handler = (e: MediaQueryListEvent) => {\r\n if (e.matches) {\r\n mql.removeEventListener('change', handler);\r\n doHydrate();\r\n }\r\n };\r\n mql.addEventListener('change', handler);\r\n }\r\n }\r\n break;\r\n\r\n case 'only':\r\n // client:only - component was not server rendered, mount fresh\r\n // For now, just hydrate (server renders a placeholder)\r\n doHydrate();\r\n break;\r\n }\r\n\r\n // Return the node after the component's closing marker\r\n return endNode ? endNode.nextSibling : dom;\r\n}\r\n\r\n/**\r\n * Find component start/end markers in DOM\r\n */\r\nfunction findComponentBoundaries(dom: Node | null): { startNode: Node | null; endNode: Node | null } {\r\n let startNode = dom;\r\n let endNode: Node | null = null;\r\n\r\n // Look for component marker <!--$c:N-->\r\n if (dom && dom.nodeType === Node.COMMENT_NODE) {\r\n const text = (dom as Comment).data;\r\n if (text.startsWith('$c:')) {\r\n const id = text.slice(3);\r\n startNode = dom;\r\n\r\n // Find the closing marker <!--/$c:N-->\r\n let current: Node | null = dom.nextSibling;\r\n while (current) {\r\n if (current.nodeType === Node.COMMENT_NODE) {\r\n const closeText = (current as Comment).data;\r\n if (closeText === `/$c:${id}`) {\r\n endNode = current;\r\n break;\r\n }\r\n }\r\n current = current.nextSibling;\r\n }\r\n }\r\n }\r\n\r\n return { startNode, endNode };\r\n}\r\n\r\n/**\r\n * Observe when a component's DOM becomes visible\r\n */\r\nfunction observeComponentVisibility(startNode: Node | null, endNode: Node | null, callback: () => void): void {\r\n // Find an element to observe between start and end markers\r\n let targetElement: Element | null = null;\r\n let current = startNode?.nextSibling || null;\r\n\r\n while (current && current !== endNode) {\r\n if (current.nodeType === Node.ELEMENT_NODE) {\r\n targetElement = current as Element;\r\n break;\r\n }\r\n current = current.nextSibling;\r\n }\r\n\r\n if (!targetElement) {\r\n // No element found, hydrate immediately\r\n callback();\r\n return;\r\n }\r\n\r\n const observer = new IntersectionObserver((entries) => {\r\n for (const entry of entries) {\r\n if (entry.isIntersecting) {\r\n observer.disconnect();\r\n callback();\r\n break;\r\n }\r\n }\r\n }, { rootMargin: '50px' });\r\n\r\n observer.observe(targetElement);\r\n}\r\n\r\n/**\r\n * Hydrate a component - run setup and create reactive effect\r\n * @param vnode - The VNode to hydrate\r\n * @param dom - The DOM node to start from\r\n * @param parent - The parent node\r\n * @param serverState - Optional state captured from server for async components\r\n */\r\nfunction hydrateComponent(vnode: VNode, dom: Node | null, parent: Node, serverState?: Record<string, any>): Node | null {\r\n const componentFactory = vnode.type as unknown as ComponentFactory;\r\n const setup = componentFactory.__setup;\r\n const componentName = componentFactory.__name || 'Anonymous';\r\n\r\n // Auto-register component for async streaming hydration\r\n if (componentName && componentName !== 'Anonymous') {\r\n registerComponent(componentName, componentFactory);\r\n }\r\n\r\n // Check if we're at a component marker comment\r\n let anchor: Comment | null = null;\r\n let componentId: number | null = null;\r\n if (dom && dom.nodeType === Node.COMMENT_NODE) {\r\n const commentText = (dom as Comment).data;\r\n if (commentText.startsWith('$c:')) {\r\n // Use this comment as the component's anchor (like runtime-dom does)\r\n anchor = dom as Comment;\r\n componentId = parseInt(commentText.slice(3), 10);\r\n dom = dom.nextSibling;\r\n\r\n // Skip island markers too\r\n while (dom && dom.nodeType === Node.COMMENT_NODE) {\r\n const text = (dom as Comment).data;\r\n if (text.startsWith('$island:')) {\r\n dom = dom.nextSibling;\r\n } else {\r\n break;\r\n }\r\n }\r\n }\r\n }\r\n\r\n // Try to get server state from island data if not provided\r\n if (!serverState && componentId !== null) {\r\n serverState = getIslandServerState(componentId);\r\n }\r\n\r\n const internalVNode = vnode as InternalVNode;\r\n const initialProps = vnode.props || {};\r\n const { children, slots: slotsFromProps, ...propsData } = filterClientDirectives(initialProps);\r\n\r\n // Create reactive props\r\n const reactiveProps = signal(propsData);\r\n internalVNode._componentProps = reactiveProps;\r\n\r\n // Create slots\r\n const slots = createSlots(children, slotsFromProps);\r\n internalVNode._slots = slots;\r\n\r\n const mountHooks: ((ctx: any) => void)[] = [];\r\n const cleanupHooks: ((ctx: any) => void)[] = [];\r\n\r\n const parentInstance = getCurrentInstance();\r\n\r\n // Use restoring signal when we have server state to restore\r\n const signalFn = serverState\r\n ? createRestoringSignal(serverState)\r\n : signal;\r\n\r\n // Create SSR helper for client-side\r\n // When hydrating with server state, ssr.load() is a no-op (data already restored)\r\n const hasServerState = !!serverState;\r\n const ssrHelper = {\r\n load(_fn: () => Promise<void>): void {\r\n // No-op on client when hydrating - signal state was restored from server\r\n // The data is already in the signals, no need to re-fetch\r\n if (hasServerState) {\r\n console.log(`[Hydrate] Skipping ssr.load() - using restored server state`);\r\n }\r\n // If no server state, this is a client-only render, but ssr.load() \r\n // shouldn't be called on pure client components anyway\r\n },\r\n isServer: false,\r\n isHydrating: hasServerState\r\n };\r\n\r\n const componentCtx: ComponentSetupContext = {\r\n el: parent as HTMLElement,\r\n signal: signalFn as typeof signal,\r\n props: createPropsAccessor(reactiveProps),\r\n slots: slots,\r\n emit: (event: string, ...args: any[]) => {\r\n const eventName = `on${event[0].toUpperCase() + event.slice(1)}`;\r\n const handler = (reactiveProps as any)[eventName];\r\n if (handler && typeof handler === 'function') {\r\n handler(...args);\r\n }\r\n },\r\n parent: parentInstance,\r\n onMount: (fn) => { mountHooks.push(fn); },\r\n onCleanup: (fn) => { cleanupHooks.push(fn); },\r\n expose: () => { },\r\n renderFn: null,\r\n update: () => { },\r\n ssr: ssrHelper,\r\n _serverState: serverState\r\n };\r\n\r\n const prev = setCurrentInstance(componentCtx);\r\n let renderFn: (() => any) | undefined;\r\n\r\n try {\r\n renderFn = setup(componentCtx);\r\n } catch (err) {\r\n console.error(`Error hydrating component ${componentName}:`, err);\r\n } finally {\r\n setCurrentInstance(prev);\r\n }\r\n\r\n // Track where the component's DOM starts (use anchor if found)\r\n const startDom = anchor || dom;\r\n let endDom: Node | null = dom;\r\n\r\n if (renderFn) {\r\n componentCtx.renderFn = renderFn;\r\n let isFirstRender = true;\r\n\r\n // Create reactive effect - on first run, hydrate; on subsequent, use render()\r\n const componentEffect = effect(() => {\r\n const prevInstance = setCurrentInstance(componentCtx);\r\n try {\r\n const subTreeResult = componentCtx.renderFn!();\r\n if (subTreeResult == null) return;\r\n\r\n const subTree = normalizeSubTree(subTreeResult);\r\n const prevSubTree = internalVNode._subTree;\r\n\r\n if (isFirstRender) {\r\n // First render - hydrate against existing DOM\r\n isFirstRender = false;\r\n endDom = hydrateNode(subTree, dom, parent);\r\n internalVNode._subTree = subTree;\r\n } else {\r\n // Subsequent renders - use runtime-dom's render for patching\r\n // The container is where prevSubTree.dom lives\r\n if (prevSubTree && prevSubTree.dom) {\r\n const container = prevSubTree.dom.parentNode as Element;\r\n if (container) {\r\n // Set _vnode so render() knows what's there\r\n (container as any)._vnode = prevSubTree;\r\n render(subTree, container);\r\n }\r\n }\r\n internalVNode._subTree = subTree;\r\n }\r\n } finally {\r\n setCurrentInstance(prevInstance);\r\n }\r\n });\r\n\r\n internalVNode._effect = componentEffect;\r\n componentCtx.update = () => componentEffect();\r\n }\r\n\r\n // Use anchor comment as the component's dom reference (like runtime-dom does)\r\n // This ensures the reconciler can use it as a stable anchor\r\n vnode.dom = anchor || startDom;\r\n\r\n // Run mount hooks\r\n const mountCtx = { el: parent as Element };\r\n mountHooks.forEach(hook => hook(mountCtx));\r\n\r\n // Store cleanup\r\n vnode.cleanup = () => {\r\n cleanupHooks.forEach(hook => hook(mountCtx));\r\n };\r\n\r\n // Skip past the end marker comment (<!--/$c:id-->)\r\n let result = endDom;\r\n while (result && result.nodeType === Node.COMMENT_NODE) {\r\n const commentText = (result as Comment).data;\r\n if (commentText.startsWith('/$c:')) {\r\n result = result.nextSibling;\r\n break;\r\n }\r\n result = result.nextSibling;\r\n }\r\n\r\n return result;\r\n}\r\n\r\n// ============= Helper Functions =============\r\n\r\nfunction filterClientDirectives(props: Record<string, any>): Record<string, any> {\r\n const filtered: Record<string, any> = {};\r\n for (const key in props) {\r\n if (!key.startsWith('client:')) {\r\n filtered[key] = props[key];\r\n }\r\n }\r\n return filtered;\r\n}\r\n\r\n/**\r\n * Fix select element value after hydrating children.\r\n * This is needed because <select>.value only works after <option> children exist in DOM.\r\n */\r\nfunction fixSelectValue(dom: HTMLElement, props: any) {\r\n if (dom.tagName === 'SELECT' && 'value' in props) {\r\n const val = props.value;\r\n if ((dom as HTMLSelectElement).multiple) {\r\n const options = (dom as HTMLSelectElement).options;\r\n const valArray = Array.isArray(val) ? val : [val];\r\n for (let i = 0; i < options.length; i++) {\r\n options[i].selected = valArray.includes(options[i].value);\r\n }\r\n } else {\r\n (dom as HTMLSelectElement).value = String(val);\r\n }\r\n }\r\n}\r\n\r\n// createPropsAccessor, createSlots, and normalizeSubTree are now imported from @sigx/runtime-core\r\n\r\n// ============= Island Hydration =============\r\n\r\n/**\r\n * Hydrate islands based on their strategies (selective hydration)\r\n */\r\nexport function hydrateIslands(): void {\r\n const dataScript = document.getElementById('__SIGX_ISLANDS__');\r\n if (!dataScript) {\r\n return;\r\n }\r\n\r\n let islandData: Record<string, IslandInfo>;\r\n try {\r\n islandData = JSON.parse(dataScript.textContent || '{}');\r\n } catch {\r\n console.error('Failed to parse island data');\r\n return;\r\n }\r\n\r\n for (const [idStr, info] of Object.entries(islandData)) {\r\n const id = parseInt(idStr, 10);\r\n scheduleHydration(id, info);\r\n }\r\n}\r\n\r\n/**\r\n * Schedule hydration based on strategy\r\n */\r\nfunction scheduleHydration(id: number, info: IslandInfo): void {\r\n const marker = findIslandMarker(id);\r\n if (!marker) {\r\n console.warn(`Island marker not found for id ${id}`);\r\n return;\r\n }\r\n\r\n const component = info.componentId ? getComponent(info.componentId) : null;\r\n if (!component && info.strategy !== 'only') {\r\n console.warn(`Component \"${info.componentId}\" not registered for hydration`);\r\n return;\r\n }\r\n\r\n switch (info.strategy) {\r\n case 'load':\r\n hydrateIsland(marker, component!, info);\r\n break;\r\n\r\n case 'idle':\r\n if ('requestIdleCallback' in window) {\r\n requestIdleCallback(() => hydrateIsland(marker, component!, info));\r\n } else {\r\n setTimeout(() => hydrateIsland(marker, component!, info), 200);\r\n }\r\n break;\r\n\r\n case 'visible':\r\n observeVisibility(marker, () => hydrateIsland(marker, component!, info));\r\n break;\r\n\r\n case 'media':\r\n if (info.media) {\r\n const mql = window.matchMedia(info.media);\r\n if (mql.matches) {\r\n hydrateIsland(marker, component!, info);\r\n } else {\r\n mql.addEventListener('change', function handler(e) {\r\n if (e.matches) {\r\n mql.removeEventListener('change', handler);\r\n hydrateIsland(marker, component!, info);\r\n }\r\n });\r\n }\r\n }\r\n break;\r\n\r\n case 'only':\r\n if (component) {\r\n mountClientOnly(marker, component, info);\r\n }\r\n break;\r\n }\r\n}\r\n\r\nfunction findIslandMarker(id: number): Comment | null {\r\n const walker = document.createTreeWalker(\r\n document.body,\r\n NodeFilter.SHOW_COMMENT,\r\n null\r\n );\r\n\r\n let node: Comment | null;\r\n while ((node = walker.nextNode() as Comment | null)) {\r\n if (node.data === `$c:${id}`) {\r\n return node;\r\n }\r\n }\r\n return null;\r\n}\r\n\r\nfunction observeVisibility(marker: Comment, callback: () => void): void {\r\n let node: Node | null = marker.nextSibling;\r\n while (node && node.nodeType !== Node.ELEMENT_NODE) {\r\n node = node.nextSibling;\r\n }\r\n\r\n if (!node) return;\r\n\r\n const observer = new IntersectionObserver((entries) => {\r\n for (const entry of entries) {\r\n if (entry.isIntersecting) {\r\n observer.disconnect();\r\n callback();\r\n break;\r\n }\r\n }\r\n });\r\n\r\n observer.observe(node as Element);\r\n}\r\n\r\nfunction hydrateIsland(marker: Comment, component: ComponentFactory, info: IslandInfo): void {\r\n // Find the container element after the marker\r\n let container: Node | null = marker.nextSibling;\r\n while (container && container.nodeType === Node.COMMENT_NODE) {\r\n container = container.nextSibling;\r\n }\r\n\r\n if (!container || container.nodeType !== Node.ELEMENT_NODE) {\r\n console.warn('No element found for island hydration');\r\n return;\r\n }\r\n\r\n const props = info.props || {};\r\n\r\n // Set pending server state so context extension picks it up\r\n if (info.state) {\r\n setPendingServerState(info.state);\r\n }\r\n\r\n // Create VNode and render with runtime-dom\r\n const vnode: VNode = {\r\n type: component as any,\r\n props: props,\r\n key: null,\r\n children: [],\r\n dom: null\r\n };\r\n\r\n // Use a wrapper to contain the component\r\n const wrapper = document.createElement('div');\r\n wrapper.style.display = 'contents';\r\n\r\n // Replace the SSR content with the hydrated component\r\n const parent = container.parentNode!;\r\n parent.insertBefore(wrapper, container);\r\n parent.removeChild(container);\r\n\r\n render(vnode, wrapper);\r\n}\r\n\r\nfunction mountClientOnly(marker: Comment, component: ComponentFactory, info: IslandInfo): void {\r\n let placeholder = marker.nextSibling;\r\n while (placeholder && placeholder.nodeType === Node.COMMENT_NODE) {\r\n placeholder = placeholder.nextSibling;\r\n }\r\n\r\n if (!placeholder || !(placeholder as Element).hasAttribute?.('data-island')) {\r\n return;\r\n }\r\n\r\n const props = info.props || {};\r\n const container = placeholder as Element;\r\n container.innerHTML = '';\r\n\r\n const vnode: VNode = {\r\n type: component as any,\r\n props,\r\n key: null,\r\n children: [],\r\n dom: null\r\n };\r\n\r\n render(vnode, container);\r\n}\r\n\r\n/**\r\n * Set up listener for async components streaming in.\r\n * This is set up at module load time to catch events early.\r\n */\r\nlet _asyncListenerSetup = false;\r\n\r\nfunction ensureAsyncHydrationListener(): void {\r\n if (_asyncListenerSetup) return;\r\n _asyncListenerSetup = true;\r\n\r\n // Listen for async component ready events\r\n document.addEventListener('sigx:async-ready', (event: Event) => {\r\n const customEvent = event as CustomEvent;\r\n const { id, state } = customEvent.detail || {};\r\n\r\n console.log(`[Hydrate] Async component ${id} ready, hydrating...`);\r\n\r\n // Invalidate cache so we get fresh island data\r\n invalidateIslandCache();\r\n\r\n // Find the placeholder element\r\n const placeholder = document.querySelector(`[data-async-placeholder=\"${id}\"]`);\r\n if (!placeholder) {\r\n console.warn(`[Hydrate] Could not find placeholder for async component ${id}`);\r\n return;\r\n }\r\n\r\n // Get the island info (should have been updated by the streaming script)\r\n const islandData = getIslandData();\r\n const info = islandData[String(id)];\r\n\r\n if (!info) {\r\n console.warn(`[Hydrate] No island data for async component ${id}`);\r\n return;\r\n }\r\n\r\n // Hydrate the async component\r\n hydrateAsyncComponent(placeholder as Element, info);\r\n });\r\n}\r\n\r\n// Set up the listener immediately when this module loads (browser only)\r\nif (typeof document !== 'undefined') {\r\n ensureAsyncHydrationListener();\r\n}\r\n\r\n/**\r\n * Hydrate an async component that just streamed in.\r\n * The DOM has already been replaced with server-rendered content by $SIGX_REPLACE.\r\n * Since the streamed content doesn't include component markers, we mount fresh\r\n * with the server state restored via the context extension.\r\n */\r\nfunction hydrateAsyncComponent(container: Element, info: IslandInfo): void {\r\n if (!info.componentId) {\r\n console.error(`[Hydrate] No componentId in island info`);\r\n return;\r\n }\r\n\r\n const component = getComponent(info.componentId);\r\n if (!component) {\r\n console.error(`[Hydrate] Component \"${info.componentId}\" not registered`);\r\n return;\r\n }\r\n\r\n const props = info.props || {};\r\n const serverState = info.state;\r\n\r\n // Clear the streamed HTML - we'll mount fresh with reactivity\r\n container.innerHTML = '';\r\n\r\n // Set pending server state so context extension picks it up\r\n if (serverState) {\r\n setPendingServerState(serverState);\r\n }\r\n\r\n // Create vnode (no need for __serverState prop, context extension handles it)\r\n const vnode: VNode = {\r\n type: component as any,\r\n props: props,\r\n key: null,\r\n children: [],\r\n dom: null\r\n };\r\n\r\n // Mount the component fresh\r\n render(vnode, container);\r\n\r\n console.log(`[Hydrate] Async component \"${info.componentId}\" mounted with server state`);\r\n}\r\n"],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Component Registry for Island Hydration
|
|
3
|
+
*
|
|
4
|
+
* Components must be registered before they can be hydrated as islands.
|
|
5
|
+
*/
|
|
6
|
+
import type { ComponentFactory } from './types.js';
|
|
7
|
+
/**
|
|
8
|
+
* Register a component for island hydration.
|
|
9
|
+
* Components must be registered before hydrateIslands() is called.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* import { registerComponent } from '@sigx/server-renderer/client';
|
|
14
|
+
* import { Counter } from './components/Counter';
|
|
15
|
+
*
|
|
16
|
+
* registerComponent('Counter', Counter);
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export declare function registerComponent(name: string, component: ComponentFactory): void;
|
|
20
|
+
/**
|
|
21
|
+
* Register multiple components at once
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```ts
|
|
25
|
+
* import { registerComponents } from '@sigx/server-renderer/client';
|
|
26
|
+
* import * as Components from './components';
|
|
27
|
+
*
|
|
28
|
+
* registerComponents(Components);
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export declare function registerComponents(components: Record<string, ComponentFactory>): void;
|
|
32
|
+
/**
|
|
33
|
+
* Get a registered component by name
|
|
34
|
+
*/
|
|
35
|
+
export declare function getComponent(name: string): ComponentFactory | undefined;
|
|
36
|
+
/**
|
|
37
|
+
* Hydration Registry class for more advanced use cases
|
|
38
|
+
*/
|
|
39
|
+
export declare class HydrationRegistry {
|
|
40
|
+
private components;
|
|
41
|
+
register(name: string, component: ComponentFactory): this;
|
|
42
|
+
registerAll(components: Record<string, ComponentFactory>): this;
|
|
43
|
+
get(name: string): ComponentFactory | undefined;
|
|
44
|
+
has(name: string): boolean;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/client/registry.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAOnD;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,gBAAgB,GAAG,IAAI,CAEjF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,GAAG,IAAI,CAMrF;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAEvE;AASD;;GAEG;AACH,qBAAa,iBAAiB;IAC1B,OAAO,CAAC,UAAU,CAAuC;IAEzD,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,gBAAgB,GAAG,IAAI;IAKzD,WAAW,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,GAAG,IAAI;IAS/D,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;IAI/C,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;CAG7B"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for client-side hydration
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Component factory with setup function
|
|
6
|
+
*/
|
|
7
|
+
export interface ComponentFactory {
|
|
8
|
+
__setup: Function;
|
|
9
|
+
__name?: string;
|
|
10
|
+
__async?: boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Hydration options
|
|
14
|
+
*/
|
|
15
|
+
export interface HydrationOptions {
|
|
16
|
+
recover?: boolean;
|
|
17
|
+
onMismatch?: (message: string, node: Node | null, vnode: any) => void;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Island information serialized from server
|
|
21
|
+
*/
|
|
22
|
+
export interface IslandInfo {
|
|
23
|
+
strategy: 'load' | 'idle' | 'visible' | 'media' | 'only';
|
|
24
|
+
media?: string;
|
|
25
|
+
props?: Record<string, any>;
|
|
26
|
+
componentId?: string;
|
|
27
|
+
/** Captured signal state from async setup for client hydration */
|
|
28
|
+
state?: Record<string, any>;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* VNode representation
|
|
32
|
+
*/
|
|
33
|
+
export interface VNode {
|
|
34
|
+
type: any;
|
|
35
|
+
props: Record<string, any>;
|
|
36
|
+
key: string | number | null;
|
|
37
|
+
children: VNode[];
|
|
38
|
+
dom: any | null;
|
|
39
|
+
text?: string | number;
|
|
40
|
+
parent?: VNode | null;
|
|
41
|
+
cleanup?: () => void;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/client/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC7B,OAAO,EAAE,QAAQ,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,IAAI,EAAE,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;CACzE;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACvB,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC;IACzD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kEAAkE;IAClE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC/B;AAED;;GAEG;AACH,MAAM,WAAW,KAAK;IAClB,IAAI,EAAE,GAAG,CAAC;IACV,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC3B,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,EAAE,KAAK,EAAE,CAAC;IAClB,GAAG,EAAE,GAAG,GAAG,IAAI,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACxB"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client hydration directive types.
|
|
3
|
+
*
|
|
4
|
+
* These directives control selective hydration behavior for SSR components.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Client hydration directive props
|
|
8
|
+
*/
|
|
9
|
+
export interface ClientDirectives {
|
|
10
|
+
/**
|
|
11
|
+
* Hydrate this component immediately when the page loads.
|
|
12
|
+
* Use for critical interactive components above the fold.
|
|
13
|
+
*/
|
|
14
|
+
'client:load'?: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Hydrate this component during browser idle time (requestIdleCallback).
|
|
17
|
+
* Use for non-critical components that don't need immediate interactivity.
|
|
18
|
+
*/
|
|
19
|
+
'client:idle'?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Hydrate this component when it enters the viewport (IntersectionObserver).
|
|
22
|
+
* Use for below-the-fold components to improve initial load performance.
|
|
23
|
+
*/
|
|
24
|
+
'client:visible'?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Hydrate this component when the media query matches.
|
|
27
|
+
* Pass the media query string as the value.
|
|
28
|
+
* @example <Sidebar client:media="(min-width: 768px)" />
|
|
29
|
+
*/
|
|
30
|
+
'client:media'?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Skip server rendering entirely; only render on client.
|
|
33
|
+
* Use for components that cannot run on the server (e.g., use browser APIs).
|
|
34
|
+
* A placeholder will be rendered on the server.
|
|
35
|
+
*/
|
|
36
|
+
'client:only'?: boolean;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* SSR helper object for async data loading.
|
|
40
|
+
* Provides clean async data loading that works seamlessly across server and client.
|
|
41
|
+
*/
|
|
42
|
+
export interface SSRHelper {
|
|
43
|
+
/**
|
|
44
|
+
* Load async data during SSR. The callback runs on the server and is skipped
|
|
45
|
+
* during client hydration (state is automatically restored from server).
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```tsx
|
|
49
|
+
* export const UserCard = defineComponent(({ signal, props, ssr }) => {
|
|
50
|
+
* const state = signal({ user: null, loading: true });
|
|
51
|
+
*
|
|
52
|
+
* ssr.load(async () => {
|
|
53
|
+
* state.user = await fetchUser(props.userId);
|
|
54
|
+
* state.loading = false;
|
|
55
|
+
* });
|
|
56
|
+
*
|
|
57
|
+
* return () => state.loading
|
|
58
|
+
* ? <div>Loading...</div>
|
|
59
|
+
* : <div>{state.user.name}</div>;
|
|
60
|
+
* });
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
load(fn: () => Promise<void>): void;
|
|
64
|
+
/**
|
|
65
|
+
* Whether we're currently running on the server (SSR context).
|
|
66
|
+
*/
|
|
67
|
+
readonly isServer: boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Whether we're hydrating on the client with server state.
|
|
70
|
+
*/
|
|
71
|
+
readonly isHydrating: boolean;
|
|
72
|
+
}
|
|
73
|
+
declare module '@sigx/runtime-core' {
|
|
74
|
+
interface ComponentAttributeExtensions extends ClientDirectives {
|
|
75
|
+
}
|
|
76
|
+
interface ComponentSetupContext {
|
|
77
|
+
/**
|
|
78
|
+
* SSR helper for async data loading.
|
|
79
|
+
* Use `ssr.load()` to fetch data that runs on server and is skipped on client hydration.
|
|
80
|
+
*/
|
|
81
|
+
ssr: SSRHelper;
|
|
82
|
+
/**
|
|
83
|
+
* @internal Map of signal names to their current values (for SSR state capture)
|
|
84
|
+
*/
|
|
85
|
+
_signals?: Map<string, any>;
|
|
86
|
+
/**
|
|
87
|
+
* @internal Pre-captured server state (for client hydration restoration)
|
|
88
|
+
*/
|
|
89
|
+
_serverState?: Record<string, any>;
|
|
90
|
+
/**
|
|
91
|
+
* @internal Array of pending SSR load promises
|
|
92
|
+
*/
|
|
93
|
+
_ssrLoads?: Promise<void>[];
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=client-directives.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-directives.d.ts","sourceRoot":"","sources":["../src/client-directives.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC7B;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAE3B;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED;;;GAGG;AACH,MAAM,WAAW,SAAS;IACtB;;;;;;;;;;;;;;;;;;;OAmBG;IACH,IAAI,CAAC,EAAE,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAEpC;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAE3B;;OAEG;IACH,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;CACjC;AAID,OAAO,QAAQ,oBAAoB,CAAC;IAEhC,UAAU,4BAA6B,SAAQ,gBAAgB;KAAI;IAGnE,UAAU,qBAAqB;QAC3B;;;WAGG;QACH,GAAG,EAAE,SAAS,CAAC;QAEf;;WAEG;QACH,QAAQ,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAE5B;;WAEG;QACH,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAEnC;;WAEG;QACH,SAAS,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;KAC/B;CACJ"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,22 +1,45 @@
|
|
|
1
|
-
import { JSXElement } from '@sigx/runtime-core';
|
|
2
1
|
/**
|
|
3
|
-
*
|
|
2
|
+
* @sigx/server-renderer
|
|
3
|
+
*
|
|
4
|
+
* Server-side rendering and client-side hydration for SigX applications.
|
|
5
|
+
*
|
|
6
|
+
* ## Server Usage
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { renderToStream, renderToString } from '@sigx/server-renderer/server';
|
|
9
|
+
*
|
|
10
|
+
* // Streaming (recommended)
|
|
11
|
+
* const stream = renderToStream(<App />);
|
|
12
|
+
*
|
|
13
|
+
* // Or string
|
|
14
|
+
* const html = await renderToString(<App />);
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* ## Client Usage
|
|
18
|
+
* ```ts
|
|
19
|
+
* import { hydrate } from '@sigx/server-renderer/client';
|
|
20
|
+
*
|
|
21
|
+
* hydrate(<App />, document.getElementById('root')!);
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* ## Selective Hydration (Islands)
|
|
25
|
+
* ```tsx
|
|
26
|
+
* // Enable JSX types for client directives
|
|
27
|
+
* import '@sigx/server-renderer/jsx';
|
|
28
|
+
*
|
|
29
|
+
* // Use directives on components
|
|
30
|
+
* <Counter client:visible /> // Hydrate when visible
|
|
31
|
+
* <Widget client:idle /> // Hydrate during idle time
|
|
32
|
+
* <Modal client:load /> // Hydrate immediately
|
|
33
|
+
* <Sidebar client:media="(min-width: 768px)" /> // Hydrate on media match
|
|
34
|
+
* <Map client:only /> // Client-only, no SSR
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* @module
|
|
4
38
|
*/
|
|
5
|
-
export
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export
|
|
10
|
-
|
|
11
|
-
* Clear the style registry (call between requests for isolation)
|
|
12
|
-
*/
|
|
13
|
-
export declare function clearStyles(): void;
|
|
14
|
-
/**
|
|
15
|
-
* Get all collected styles as an array
|
|
16
|
-
*/
|
|
17
|
-
export declare function getAllStyles(): Array<{
|
|
18
|
-
id: string;
|
|
19
|
-
cssText: string;
|
|
20
|
-
}>;
|
|
21
|
-
export declare function renderToString(element: JSXElement): Promise<string>;
|
|
39
|
+
export { renderToStream, renderToString } from './server/index.js';
|
|
40
|
+
export { createSSRContext } from './server/context.js';
|
|
41
|
+
export type { SSRContext, SSRContextOptions, RenderOptions, IslandInfo, HydrationStrategy } from './server/context.js';
|
|
42
|
+
export { hydrate, hydrateIslands } from './client/index.js';
|
|
43
|
+
export { registerComponent, registerComponents, HydrationRegistry } from './client/registry.js';
|
|
44
|
+
export type { HydrationOptions } from './client/hydrate.js';
|
|
22
45
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAGH,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,aAAa,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAGvH,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAChG,YAAY,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC"}
|