frontend-hamroun 1.1.54 → 1.1.56

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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sources":["../src/batch.ts","../src/hooks.ts","../src/jsx-runtime.ts","../src/context.ts","../src/renderer.ts","../src/templates/default.ts","../src/server-renderer.ts","../src/router.ts","../src/link.tsx"],"sourcesContent":["export let isBatching = false;\r\nconst queue: Function[] = [];\r\n\r\nexport function batchUpdates(fn: Function) {\r\n if (isBatching) {\r\n queue.push(fn);\r\n return;\r\n }\r\n\r\n isBatching = true;\r\n try {\r\n fn();\r\n while (queue.length > 0) {\r\n const nextFn = queue.shift();\r\n nextFn?.();\r\n }\r\n } finally {\r\n isBatching = false;\r\n }\r\n}\r\n\r\nexport function getIsBatching() {\r\n return isBatching;\r\n}\r\n","import { createElement } from './jsx-runtime';\r\nimport { batchUpdates, isBatching } from './batch';\r\nimport { diff } from './vdom';\r\n\r\nlet currentRender: number = 0;\r\nconst states = new Map<number, any[]>();\r\nconst stateIndices = new Map<number, number>();\r\nconst effects = new Map<number, Effect[]>();\r\nconst memos = new Map<number, { value: any; deps: any[] }[]>();\r\nconst refs = new Map<number, any[]>();\r\n\r\ninterface Effect {\r\n cleanup?: () => void;\r\n deps?: any[];\r\n}\r\n\r\n// Add at the top with other declarations\r\nlet globalRenderCallback: ((element: any, container: HTMLElement) => void) | null = null;\r\nlet globalContainer: HTMLElement | null = null;\r\nlet currentElement: any = null;\r\n\r\n// Add environment detection\r\nconst isServer = typeof window === 'undefined';\r\n\r\nconst serverStates = new Map<number, any>();\r\n\r\nexport function setRenderCallback(\r\n callback: (element: any, container: HTMLElement) => void,\r\n element: any,\r\n container: HTMLElement\r\n) {\r\n globalRenderCallback = callback;\r\n globalContainer = container;\r\n currentElement = element;\r\n}\r\n\r\nexport function prepareRender() {\r\n currentRender++;\r\n stateIndices.set(currentRender, 0);\r\n return currentRender;\r\n}\r\n\r\nexport function finishRender() {\r\n if (isServer) {\r\n serverStates.delete(currentRender);\r\n }\r\n currentRender = 0;\r\n}\r\n\r\nexport function useState<T>(initial: T): [T, (value: T | ((prev: T) => T)) => void] {\r\n if (!currentRender) {\r\n throw new Error('useState must be called within a render');\r\n }\r\n\r\n if (isServer) {\r\n // Server-side state handling\r\n if (!serverStates.has(currentRender)) {\r\n serverStates.set(currentRender, new Map());\r\n }\r\n const componentState = serverStates.get(currentRender)!;\r\n const index = stateIndices.get(currentRender) || 0;\r\n \r\n if (!componentState.has(index)) {\r\n componentState.set(index, initial);\r\n }\r\n\r\n const state = componentState.get(index);\r\n const setState = (newValue: T | ((prev: T) => T)) => {\r\n // No-op for server-side\r\n };\r\n\r\n stateIndices.set(currentRender, index + 1);\r\n return [state, setState];\r\n }\r\n\r\n if (!states.has(currentRender)) {\r\n states.set(currentRender, []);\r\n }\r\n\r\n const componentStates = states.get(currentRender)!;\r\n const index = stateIndices.get(currentRender)!;\r\n \r\n if (index >= componentStates.length) {\r\n componentStates.push(initial);\r\n }\r\n\r\n const state = componentStates[index];\r\n const setState = (newValue: T | ((prev: T) => T)) => {\r\n const nextValue = typeof newValue === 'function' \r\n ? (newValue as Function)(componentStates[index])\r\n : newValue;\r\n\r\n if (componentStates[index] === nextValue) return; // Skip if value hasn't changed\r\n \r\n componentStates[index] = nextValue;\r\n \r\n if (isBatching) {\r\n batchUpdates(() => rerender(currentRender));\r\n } else {\r\n rerender(currentRender);\r\n }\r\n };\r\n\r\n stateIndices.set(currentRender, index + 1);\r\n return [state, setState];\r\n}\r\n\r\nexport function useEffect(callback: () => (() => void) | void, deps?: any[]) {\r\n if (!currentRender) throw new Error('useEffect must be called within a render');\r\n \r\n // Skip effect execution on server\r\n if (isServer) {\r\n // Just increment hook index for SSR consistency\r\n const effectIndex = stateIndices.get(currentRender)!;\r\n stateIndices.set(currentRender, effectIndex + 1);\r\n return;\r\n }\r\n \r\n const effectIndex = stateIndices.get(currentRender)!;\r\n \r\n if (!effects.has(currentRender)) {\r\n effects.set(currentRender, []);\r\n }\r\n\r\n const componentEffects = effects.get(currentRender)!;\r\n const prevEffect = componentEffects[effectIndex];\r\n \r\n // Run effect if deps changed\r\n if (!prevEffect || !deps || !prevEffect.deps || \r\n deps.some((dep, i) => dep !== prevEffect.deps![i])) {\r\n \r\n // Cleanup previous effect\r\n if (prevEffect?.cleanup) {\r\n prevEffect.cleanup();\r\n }\r\n\r\n // Schedule new effect\r\n queueMicrotask(() => {\r\n const cleanup = callback() || undefined;\r\n componentEffects[effectIndex] = { cleanup: cleanup, deps };\r\n });\r\n }\r\n \r\n stateIndices.set(currentRender, effectIndex + 1);\r\n}\r\n\r\nexport function useMemo<T>(factory: () => T, deps: any[]): T {\r\n if (!currentRender) throw new Error('useMemo must be called within a render');\r\n \r\n const memoIndex = stateIndices.get(currentRender)!;\r\n \r\n if (!memos.has(currentRender)) {\r\n memos.set(currentRender, []);\r\n }\r\n\r\n const componentMemos = memos.get(currentRender)!;\r\n const prevMemo = componentMemos[memoIndex];\r\n \r\n if (!prevMemo || (deps && deps.some((dep, i) => !Object.is(dep, prevMemo.deps[i])))) {\r\n const value = factory();\r\n componentMemos[memoIndex] = { value, deps };\r\n stateIndices.set(currentRender, memoIndex + 1);\r\n return value;\r\n }\r\n\r\n stateIndices.set(currentRender, memoIndex + 1);\r\n return prevMemo.value;\r\n}\r\n\r\nexport function useRef<T>(initial: T) {\r\n if (!currentRender) throw new Error('useRef must be called within a render');\r\n \r\n const refIndex = stateIndices.get(currentRender)!;\r\n \r\n if (!refs.has(currentRender)) {\r\n refs.set(currentRender, []);\r\n }\r\n\r\n const componentRefs = refs.get(currentRender)!;\r\n if (refIndex >= componentRefs.length) {\r\n // Initialize with an object that has a current property\r\n const ref = { current: initial };\r\n componentRefs.push(ref);\r\n stateIndices.set(currentRender, refIndex + 1);\r\n return ref;\r\n }\r\n\r\n const ref = componentRefs[refIndex];\r\n stateIndices.set(currentRender, refIndex + 1);\r\n return ref;\r\n}\r\n\r\n// Add a map to track component DOM nodes\r\nconst componentNodes = new Map<Function, Node>();\r\n\r\nasync function rerender(rendererId: number) {\r\n try {\r\n // Clean up effects\r\n const componentEffects = effects.get(rendererId);\r\n if (componentEffects) {\r\n componentEffects.forEach(effect => {\r\n if (effect.cleanup) effect.cleanup();\r\n });\r\n effects.set(rendererId, []);\r\n }\r\n\r\n if (globalRenderCallback && globalContainer && currentElement) {\r\n await globalRenderCallback(currentElement, globalContainer);\r\n }\r\n } catch (error) {\r\n console.error('Error during rerender:', error);\r\n }\r\n}\r\n\r\n// Add new hook for error boundaries\r\nexport function useErrorBoundary(): [Error | null, () => void] {\r\n const [error, setError] = useState<Error | null>(null);\r\n return [error, () => setError(null)];\r\n}\r\n\r\n// Define RouterState interface\r\ninterface RouterState {\r\n pathname: string;\r\n search: string;\r\n hash: string;\r\n params: Record<string, string>;\r\n}\r\n\r\n// Make router SSR-compatible\r\nexport function useRouter() {\r\n // Server-side default state\r\n const defaultState: RouterState = {\r\n pathname: '/',\r\n search: '',\r\n hash: '',\r\n params: {}\r\n };\r\n \r\n // Use current path in SSR context\r\n if (isServer) {\r\n // Try to use URL from context or fallback to default\r\n return {\r\n ...defaultState,\r\n push: () => {},\r\n replace: () => {},\r\n back: () => {},\r\n forward: () => {}\r\n };\r\n }\r\n \r\n // ...existing client-side router logic...\r\n}\r\n","import type { Component } from './component';\r\n\r\ninterface VNode {\r\n type: string | Function;\r\n props: Record<string, any>;\r\n}\r\n\r\nfunction jsx(type: string | Function, props: any): VNode {\r\n console.log('JSX Transform:', { type, props });\r\n const processedProps = { ...props };\r\n \r\n // Handle children properly\r\n if (arguments.length > 2) {\r\n processedProps.children = Array.prototype.slice.call(arguments, 2);\r\n }\r\n \r\n return { type, props: processedProps };\r\n}\r\n\r\nconst Fragment = ({ children }: { children: any }) => children;\r\n\r\nasync function createElement(vnode: VNode | any): Promise<Node> {\r\n console.log('Creating element from:', vnode);\r\n\r\n // Handle primitives and null\r\n if (vnode == null) {\r\n return document.createTextNode('');\r\n }\r\n \r\n if (typeof vnode === 'boolean') {\r\n return document.createTextNode('');\r\n }\r\n\r\n if (typeof vnode === 'number' || typeof vnode === 'string') {\r\n return document.createTextNode(String(vnode));\r\n }\r\n\r\n // Handle arrays\r\n if (Array.isArray(vnode)) {\r\n const fragment = document.createDocumentFragment();\r\n for (const child of vnode) {\r\n const node = await createElement(child);\r\n fragment.appendChild(node);\r\n }\r\n return fragment;\r\n }\r\n\r\n // Handle VNode\r\n if ('type' in vnode && vnode.props !== undefined) {\r\n const { type, props } = vnode;\r\n \r\n // Handle function components\r\n if (typeof type === 'function') {\r\n try {\r\n const result = await type(props || {});\r\n const node = await createElement(result);\r\n if (node instanceof Element) {\r\n node.setAttribute('data-component-id', type.name || type.toString());\r\n }\r\n return node;\r\n } catch (error) {\r\n console.error('Error rendering component:', error);\r\n return document.createTextNode('');\r\n }\r\n }\r\n\r\n // Create DOM element\r\n const element = document.createElement(type as string);\r\n \r\n // Handle props\r\n for (const [key, value] of Object.entries(props || {})) {\r\n if (key === 'children') continue;\r\n if (key.startsWith('on') && typeof value === 'function') {\r\n const eventName = key.toLowerCase().slice(2);\r\n // Remove existing event listener if any\r\n const existingHandler = (element as any).__events?.[eventName];\r\n if (existingHandler) {\r\n element.removeEventListener(eventName, existingHandler);\r\n }\r\n \r\n // Add new event listener\r\n element.addEventListener(eventName, value as EventListener);\r\n if (!(element as any).__events) {\r\n (element as any).__events = {};\r\n }\r\n (element as any).__events[eventName] = value;\r\n } else if (key === 'style' && typeof value === 'object') {\r\n Object.assign(element.style, value);\r\n } else if (key === 'className') {\r\n element.setAttribute('class', String(value));\r\n } else if (key !== 'key' && key !== 'ref') {\r\n element.setAttribute(key, String(value));\r\n }\r\n }\r\n\r\n // Handle children\r\n const children = props?.children;\r\n if (children != null) {\r\n const childArray = Array.isArray(children) ? children.flat() : [children];\r\n for (const child of childArray) {\r\n const childNode = await createElement(child);\r\n element.appendChild(childNode);\r\n }\r\n }\r\n\r\n return element;\r\n }\r\n\r\n // Handle other objects by converting to string\r\n return document.createTextNode(String(vnode));\r\n}\r\n\r\n// Export named functions and aliases without duplicates\r\nexport {\r\n jsx,\r\n jsx as jsxs,\r\n jsx as jsxDEV,\r\n Fragment,\r\n createElement\r\n};\r\n\r\n// Named exports object\r\nconst jsxRuntime = {\r\n jsx,\r\n jsxs: jsx,\r\n jsxDEV: jsx,\r\n Fragment,\r\n createElement\r\n};\r\n\r\nexport default jsxRuntime;\r\n","\r\n\r\nconst contexts = new Map<symbol, any>();\r\nlet currentRender: Function | null = null;\r\n\r\nexport interface Context<T> {\r\n Provider: (props: { value: T; children?: any }) => any;\r\n Consumer: (props: { children: (value: T) => any }) => any;\r\n _id: symbol;\r\n useSelector: <S>(selector: (state: T) => S) => S;\r\n}\r\n\r\nexport function createContext<T>(defaultValue: T): Context<T> {\r\n const context = {\r\n Provider: ({ value, children }: { value: T, children?: any }) => {\r\n return children;\r\n },\r\n Consumer: ({ children }: { children: (value: T) => any }) => {\r\n return children(defaultValue);\r\n },\r\n _id: Symbol(),\r\n useSelector: <S>(selector: (state: T) => S) => {\r\n return selector(defaultValue);\r\n }\r\n };\r\n\r\n return context;\r\n}\r\n\r\nexport function useContext<T>(context: any): T {\r\n return context;\r\n}\r\n","import { createElement } from './jsx-runtime';\r\nimport { prepareRender, finishRender, setRenderCallback } from './hooks';\r\nimport { batchUpdates } from './batch';\r\n\r\nlet isHydrating = false;\r\n\r\nexport async function hydrate(element: any, container: HTMLElement) {\r\n isHydrating = true;\r\n try {\r\n await render(element, container);\r\n } finally {\r\n isHydrating = false;\r\n }\r\n}\r\n\r\nexport async function render(element: any, container: HTMLElement) {\r\n console.log('Rendering to:', container.id);\r\n \r\n batchUpdates(async () => {\r\n const rendererId = prepareRender();\r\n try {\r\n setRenderCallback(render, element, container);\r\n const domNode = await createElement(element);\r\n \r\n if (!isHydrating) {\r\n container.innerHTML = '';\r\n }\r\n container.appendChild(domNode);\r\n \r\n } finally {\r\n finishRender();\r\n }\r\n });\r\n}\r\n","export interface ScriptConfig {\r\n src: string;\r\n defer?: boolean;\r\n async?: boolean;\r\n type?: string;\r\n}\r\n\r\nexport interface TemplateOptions {\r\n title: string;\r\n description?: string;\r\n scripts?: (string | ScriptConfig)[];\r\n styles?: string[];\r\n initialState?: any;\r\n meta?: Record<string, string>;\r\n bodyAttrs?: Record<string, string>;\r\n htmlAttrs?: Record<string, string>;\r\n}\r\n\r\nexport function defaultTemplate(content: string, options: TemplateOptions): string {\r\n const {\r\n title,\r\n description = '',\r\n scripts = [],\r\n styles = [],\r\n initialState = {},\r\n meta = {},\r\n bodyAttrs = {},\r\n htmlAttrs = { lang: 'en' }\r\n } = options;\r\n\r\n const htmlAttributes = Object.entries(htmlAttrs)\r\n .map(([key, value]) => `${key}=\"${value}\"`)\r\n .join(' ');\r\n\r\n const bodyAttributes = Object.entries(bodyAttrs)\r\n .map(([key, value]) => `${key}=\"${value}\"`)\r\n .join(' ');\r\n\r\n const metaTags = Object.entries(meta)\r\n .map(([name, content]) => ` <meta name=\"${name}\" content=\"${content}\">`)\r\n .join('\\n');\r\n\r\n const scriptTags = scripts.map(script => {\r\n if (typeof script === 'string') {\r\n return ` <script src=\"${script}\" defer></script>`;\r\n }\r\n const { src, defer = true, async = false, type } = script;\r\n return ` <script src=\"${src}\"${defer ? ' defer' : ''}${async ? ' async' : ''}${type ? ` type=\"${type}\"` : ''}></script>`;\r\n }).join('\\n');\r\n\r\n return `<!DOCTYPE html>\r\n<html ${htmlAttributes}>\r\n <head>\r\n <meta charset=\"UTF-8\">\r\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n ${description ? `<meta name=\"description\" content=\"${description}\">\\n` : ''}${metaTags}\r\n <title>${title}</title>\r\n${styles.map(href => ` <link rel=\"stylesheet\" href=\"${href}\">`).join('\\n')}\r\n <script>\r\n window.__INITIAL_STATE__ = ${JSON.stringify(initialState)};\r\n </script>\r\n </head>\r\n <body ${bodyAttributes}>\r\n <div id=\"root\">${content}</div>\r\n${scriptTags}\r\n </body>\r\n</html>`;\r\n}\r\n","import { VNode } from './types';\r\nimport { defaultTemplate, TemplateOptions, ScriptConfig } from './templates/default';\r\n\r\n// Track component instances during SSR\r\nconst componentStack: any[] = [];\r\nlet currentComponent: any = null;\r\n\r\nexport async function renderToString(\r\n element: JSX.Element | any,\r\n options: Partial<TemplateOptions> = {}\r\n): Promise<string> {\r\n componentStack.length = 0;\r\n currentComponent = null;\r\n\r\n try {\r\n // Handle JSX elements and Component functions differently\r\n let content: string;\r\n \r\n if (typeof element === 'function') {\r\n // If element is a component function, call it\r\n const rendered = await element({});\r\n content = await renderNodeToString(rendered);\r\n } else {\r\n // Otherwise treat as VNode or primitive\r\n content = await renderNodeToString(element);\r\n }\r\n\r\n const processedScripts = options.scripts?.map(script => \r\n typeof script === 'string' \r\n ? { src: script, defer: true } as ScriptConfig\r\n : { ...script, defer: script.defer ?? true }\r\n );\r\n\r\n // Generate the full HTML document\r\n return defaultTemplate(content, {\r\n title: 'Frontend Hamroun App',\r\n ...options,\r\n scripts: processedScripts\r\n });\r\n } catch (error) {\r\n console.error('Server rendering error:', error);\r\n // Return error page in development\r\n if (process.env.NODE_ENV === 'development') {\r\n const errorMessage = error instanceof Error \r\n ? error.stack || error.message \r\n : String(error);\r\n \r\n return `\r\n <!DOCTYPE html>\r\n <html>\r\n <head><title>Rendering Error</title></head>\r\n <body>\r\n <h1>Server Rendering Error</h1>\r\n <pre>${escapeHtml(errorMessage)}</pre>\r\n </body>\r\n </html>\r\n `;\r\n }\r\n return '';\r\n } finally {\r\n // Cleanup\r\n componentStack.length = 0;\r\n currentComponent = null;\r\n }\r\n}\r\n\r\n// Add support for Component instances\r\nasync function renderNodeToString(node: VNode | any): Promise<string> {\r\n // Handle null and boolean\r\n if (node == null || typeof node === 'boolean') return '';\r\n\r\n // Handle Component instances with render method\r\n if (node && typeof node === 'object' && typeof node.render === 'function') {\r\n try {\r\n const result = await node.render();\r\n return renderNodeToString(result);\r\n } catch (error) {\r\n console.error('Error rendering component instance:', error);\r\n return '';\r\n }\r\n }\r\n\r\n // Handle primitives\r\n if (typeof node === 'number' || typeof node === 'string') {\r\n return escapeHtml(String(node));\r\n }\r\n\r\n // Handle arrays\r\n if (Array.isArray(node)) {\r\n const results = await Promise.all(\r\n node.map(child => renderNodeToString(child))\r\n );\r\n return results.join('');\r\n }\r\n\r\n // Handle functional components\r\n if (typeof node === 'function') {\r\n const prevComponent = currentComponent;\r\n currentComponent = { type: node.name || 'Anonymous' };\r\n componentStack.push(currentComponent);\r\n\r\n try {\r\n const result = await node({});\r\n return renderNodeToString(result);\r\n } finally {\r\n componentStack.pop();\r\n currentComponent = prevComponent;\r\n }\r\n }\r\n\r\n // Handle VNodes\r\n if (node.type) {\r\n if (typeof node.type === 'function') {\r\n const prevComponent = currentComponent;\r\n currentComponent = {\r\n type: node.type.name || 'Anonymous',\r\n props: node.props || {}\r\n };\r\n componentStack.push(currentComponent);\r\n\r\n try {\r\n const result = await node.type(node.props || {});\r\n return renderNodeToString(result);\r\n } finally {\r\n componentStack.pop();\r\n currentComponent = prevComponent;\r\n }\r\n }\r\n\r\n // Handle HTML elements\r\n let html = `<${node.type}`;\r\n \r\n // Add props\r\n if (node.props) {\r\n html += renderProps(node.props);\r\n }\r\n\r\n // Self-closing tags\r\n const voidElements = new Set([\r\n 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',\r\n 'link', 'meta', 'param', 'source', 'track', 'wbr'\r\n ]);\r\n\r\n if (voidElements.has(node.type)) {\r\n return html + '/>';\r\n }\r\n\r\n html += '>';\r\n\r\n // Render children\r\n if (node.props?.children) {\r\n const children = Array.isArray(node.props.children)\r\n ? node.props.children\r\n : [node.props.children];\r\n\r\n for (const child of children) {\r\n html += await renderNodeToString(child);\r\n }\r\n }\r\n\r\n return html + `</${node.type}>`;\r\n }\r\n\r\n // Handle other cases\r\n return '';\r\n}\r\n\r\nfunction renderProps(props: Record<string, any>): string {\r\n let html = '';\r\n\r\n for (const [key, value] of Object.entries(props)) {\r\n // Skip special props\r\n if (key === 'children' || key === 'key' || key === 'ref') continue;\r\n\r\n // Handle className\r\n if (key === 'className') {\r\n if (value) html += ` class=\"${escapeHtml(String(value))}\"`;\r\n continue;\r\n }\r\n\r\n // Handle style objects\r\n if (key === 'style' && typeof value === 'object' && value !== null) {\r\n html += ` style=\"${styleToString(value)}\"`;\r\n continue;\r\n }\r\n\r\n // Skip event handlers\r\n if (key.startsWith('on')) continue;\r\n\r\n // Handle boolean attributes\r\n if (typeof value === 'boolean') {\r\n if (value) html += ` ${key}`;\r\n continue;\r\n }\r\n\r\n // Handle other attributes\r\n if (value != null) {\r\n html += ` ${key}=\"${escapeHtml(String(value))}\"`;\r\n }\r\n }\r\n\r\n return html;\r\n}\r\n\r\nfunction escapeHtml(str: string): string {\r\n return str\r\n .replace(/&/g, '&amp;')\r\n .replace(/</g, '&lt;')\r\n .replace(/>/g, '&gt;')\r\n .replace(/\"/g, '&quot;')\r\n .replace(/'/g, '&#039;');\r\n}\r\n\r\nfunction styleToString(style: Record<string, any>): string {\r\n return Object.entries(style)\r\n .map(([key, value]) => `${hyphenate(key)}: ${value}`)\r\n .join('; ');\r\n}\r\n\r\n// Convert camelCase to kebab-case\r\nfunction hyphenate(str: string): string {\r\n return str.replace(/[A-Z]/g, m => '-' + m.toLowerCase());\r\n}\r\n","import { useState, useEffect } from './hooks';\r\n\r\ninterface RouterState {\r\n pathname: string;\r\n search: string;\r\n hash: string;\r\n params: Record<string, string>;\r\n}\r\n\r\ninterface RouterOptions {\r\n replace?: boolean;\r\n scroll?: boolean;\r\n}\r\n\r\nlet currentState: RouterState = {\r\n pathname: typeof window !== 'undefined' ? window.location.pathname : '/',\r\n search: typeof window !== 'undefined' ? window.location.search : '',\r\n hash: typeof window !== 'undefined' ? window.location.hash : '',\r\n params: {}\r\n};\r\n\r\nconst listeners = new Set<(state: RouterState) => void>();\r\n\r\n// Parse route pattern and extract params\r\nfunction matchRoute(pattern: string, pathname: string): Record<string, string> | null {\r\n const paramNames: string[] = [];\r\n const regexPattern = pattern.replace(/:([^/]+)/g, (_, name) => {\r\n paramNames.push(name);\r\n return '([^/]+)';\r\n });\r\n\r\n const match = pathname.match(new RegExp(`^${regexPattern}$`));\r\n if (!match) return null;\r\n\r\n const params: Record<string, string> = {};\r\n paramNames.forEach((name, i) => {\r\n params[name] = match[i + 1];\r\n });\r\n\r\n return params;\r\n}\r\n\r\nfunction updateState(newPath: string, options: RouterOptions = {}) {\r\n const url = new URL(newPath, window.location.origin);\r\n const newState: RouterState = {\r\n pathname: url.pathname,\r\n search: url.search,\r\n hash: url.hash,\r\n params: {}\r\n };\r\n\r\n if (options.replace) {\r\n window.history.replaceState(newState, '', url.toString());\r\n } else {\r\n window.history.pushState(newState, '', url.toString());\r\n }\r\n\r\n currentState = newState;\r\n listeners.forEach(listener => listener(newState));\r\n\r\n if (options.scroll) {\r\n window.scrollTo(0, 0);\r\n }\r\n}\r\n\r\nexport function navigate(to: string, options: RouterOptions = {}) {\r\n if (typeof window === 'undefined') return;\r\n updateState(to, options);\r\n}\r\n\r\nexport function useRouter() {\r\n const [state, setState] = useState<RouterState>(currentState);\r\n\r\n useEffect(() => {\r\n const handleRouteChange = (newState: RouterState) => {\r\n setState(newState);\r\n };\r\n\r\n const handlePopState = () => {\r\n currentState = {\r\n pathname: window.location.pathname,\r\n search: window.location.search,\r\n hash: window.location.hash,\r\n params: {}\r\n };\r\n setState(currentState);\r\n };\r\n\r\n listeners.add(handleRouteChange);\r\n window.addEventListener('popstate', handlePopState);\r\n\r\n return () => {\r\n listeners.delete(handleRouteChange);\r\n window.removeEventListener('popstate', handlePopState);\r\n };\r\n }, []);\r\n\r\n return {\r\n ...state,\r\n push: (to: string, options?: RouterOptions) => navigate(to, options),\r\n replace: (to: string, options?: RouterOptions) => navigate(to, { ...options, replace: true }),\r\n back: () => window.history.back(),\r\n forward: () => window.history.forward()\r\n };\r\n}\r\n\r\nexport function useRoute(pattern: string) {\r\n const { pathname } = useRouter();\r\n const [params, setParams] = useState<Record<string, string>>({});\r\n const [isMatch, setIsMatch] = useState(false);\r\n\r\n useEffect(() => {\r\n const matchedParams = matchRoute(pattern, pathname);\r\n setIsMatch(!!matchedParams);\r\n setParams(matchedParams || {});\r\n }, [pattern, pathname]);\r\n\r\n return { isMatch, params };\r\n}\r\n\r\nexport function useParams<T extends Record<string, string>>() {\r\n const router = useRouter();\r\n return router.params as T;\r\n}\r\n\r\nexport function useSearchParams() {\r\n const { search } = useRouter();\r\n return new URLSearchParams(search);\r\n}\r\n","import { navigate } from './router';\r\n\r\ninterface LinkProps {\r\n href: string;\r\n children?: any;\r\n replace?: boolean;\r\n className?: string;\r\n style?: Record<string, string>;\r\n [key: string]: any;\r\n}\r\n\r\nexport function Link({ \r\n href, \r\n children, \r\n replace = false, \r\n className,\r\n style,\r\n ...rest \r\n}: LinkProps) {\r\n const handleClick = (e: MouseEvent) => {\r\n e.preventDefault();\r\n navigate(href, { replace });\r\n };\r\n\r\n return {\r\n type: 'a',\r\n props: {\r\n href,\r\n onClick: handleClick,\r\n className,\r\n style,\r\n ...rest,\r\n children\r\n }\r\n };\r\n}\r\n"],"names":["index","state","setState","effectIndex","ref","content"],"mappings":"AAAO,IAAI,aAAa;AACxB,MAAM,QAAoB,CAAA;AAEnB,SAAS,aAAa,IAAc;AACzC,MAAI,YAAY;AACd,UAAM,KAAK,EAAE;AACb;AAAA,EACF;AAEa,eAAA;AACT,MAAA;AACC;AACI,WAAA,MAAM,SAAS,GAAG;AACjB,YAAA,SAAS,MAAM;AACZ;AAAA,IACX;AAAA,EAAA,UACA;AACa,iBAAA;AAAA,EACf;AACF;ACfA,IAAI,gBAAwB;AAC5B,MAAM,6BAAa;AACnB,MAAM,mCAAmB;AACzB,MAAM,8BAAc;AACpB,MAAM,4BAAY;AAClB,MAAM,2BAAW;AAQjB,IAAI,uBAAgF;AACpF,IAAI,kBAAsC;AAC1C,IAAI,iBAAsB;AAG1B,MAAM,WAAW,OAAO,WAAW;AAEnC,MAAM,mCAAmB;AAET,SAAA,kBACd,UACA,SACA,WACA;AACuB,yBAAA;AACL,oBAAA;AACD,mBAAA;AACnB;AAEO,SAAS,gBAAgB;AAC9B;AACa,eAAA,IAAI,eAAe,CAAC;AAC1B,SAAA;AACT;AAEO,SAAS,eAAe;AAC7B,MAAI,UAAU;AACZ,iBAAa,OAAO,aAAa;AAAA,EACnC;AACgB,kBAAA;AAClB;AAEO,SAAS,SAAY,SAAwD;AAClF,MAAI,CAAC,eAAe;AACZ,UAAA,IAAI,MAAM,yCAAyC;AAAA,EAC3D;AAEA,MAAI,UAAU;AAEZ,QAAI,CAAC,aAAa,IAAI,aAAa,GAAG;AACpC,mBAAa,IAAI,eAAmB,oBAAA,IAAK,CAAA;AAAA,IAC3C;AACM,UAAA,iBAAiB,aAAa,IAAI,aAAa;AACrD,UAAMA,SAAQ,aAAa,IAAI,aAAa,KAAK;AAEjD,QAAI,CAAC,eAAe,IAAIA,MAAK,GAAG;AACf,qBAAA,IAAIA,QAAO,OAAO;AAAA,IACnC;AAEMC,UAAAA,SAAQ,eAAe,IAAID,MAAK;AAChCE,UAAAA,YAAW,CAAC,aAAmC;AAAA,IAAA;AAIxC,iBAAA,IAAI,eAAeF,SAAQ,CAAC;AAClC,WAAA,CAACC,QAAOC,SAAQ;AAAA,EACzB;AAEA,MAAI,CAAC,OAAO,IAAI,aAAa,GAAG;AACvB,WAAA,IAAI,eAAe,CAAA,CAAE;AAAA,EAC9B;AAEM,QAAA,kBAAkB,OAAO,IAAI,aAAa;AAC1C,QAAA,QAAQ,aAAa,IAAI,aAAa;AAExC,MAAA,SAAS,gBAAgB,QAAQ;AACnC,oBAAgB,KAAK,OAAO;AAAA,EAC9B;AAEM,QAAA,QAAQ,gBAAgB,KAAK;AAC7B,QAAA,WAAW,CAAC,aAAmC;AAC7C,UAAA,YAAY,OAAO,aAAa,aACjC,SAAsB,gBAAgB,KAAK,CAAC,IAC7C;AAEA,QAAA,gBAAgB,KAAK,MAAM;AAAW;AAE1C,oBAAgB,KAAK,IAAI;AAEzB,QAAI,YAAY;AACD,mBAAA,MAAM,SAAS,aAAa,CAAC;AAAA,IAAA,OACrC;AACL,eAAS,aAAa;AAAA,IACxB;AAAA,EAAA;AAGW,eAAA,IAAI,eAAe,QAAQ,CAAC;AAClC,SAAA,CAAC,OAAO,QAAQ;AACzB;AAEgB,SAAA,UAAU,UAAqC,MAAc;AAC3E,MAAI,CAAC;AAAqB,UAAA,IAAI,MAAM,0CAA0C;AAG9E,MAAI,UAAU;AAENC,UAAAA,eAAc,aAAa,IAAI,aAAa;AACrC,iBAAA,IAAI,eAAeA,eAAc,CAAC;AAC/C;AAAA,EACF;AAEM,QAAA,cAAc,aAAa,IAAI,aAAa;AAElD,MAAI,CAAC,QAAQ,IAAI,aAAa,GAAG;AACvB,YAAA,IAAI,eAAe,CAAA,CAAE;AAAA,EAC/B;AAEM,QAAA,mBAAmB,QAAQ,IAAI,aAAa;AAC5C,QAAA,aAAa,iBAAiB,WAAW;AAG/C,MAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,WAAW,QACpC,KAAK,KAAK,CAAC,KAAK,MAAM,QAAQ,WAAW,KAAM,CAAC,CAAC,GAAG;AAGtD,QAAI,yCAAY,SAAS;AACvB,iBAAW,QAAQ;AAAA,IACrB;AAGA,mBAAe,MAAM;AACb,YAAA,UAAU,cAAc;AAC9B,uBAAiB,WAAW,IAAI,EAAE,SAAkB,KAAK;AAAA,IAAA,CAC1D;AAAA,EACH;AAEa,eAAA,IAAI,eAAe,cAAc,CAAC;AACjD;AAEgB,SAAA,QAAW,SAAkB,MAAgB;AAC3D,MAAI,CAAC;AAAqB,UAAA,IAAI,MAAM,wCAAwC;AAEtE,QAAA,YAAY,aAAa,IAAI,aAAa;AAEhD,MAAI,CAAC,MAAM,IAAI,aAAa,GAAG;AACvB,UAAA,IAAI,eAAe,CAAA,CAAE;AAAA,EAC7B;AAEM,QAAA,iBAAiB,MAAM,IAAI,aAAa;AACxC,QAAA,WAAW,eAAe,SAAS;AAEzC,MAAI,CAAC,YAAa,QAAQ,KAAK,KAAK,CAAC,KAAK,MAAM,CAAC,OAAO,GAAG,KAAK,SAAS,KAAK,CAAC,CAAC,CAAC,GAAI;AACnF,UAAM,QAAQ;AACd,mBAAe,SAAS,IAAI,EAAE,OAAO,KAAK;AAC7B,iBAAA,IAAI,eAAe,YAAY,CAAC;AACtC,WAAA;AAAA,EACT;AAEa,eAAA,IAAI,eAAe,YAAY,CAAC;AAC7C,SAAO,SAAS;AAClB;AAEO,SAAS,OAAU,SAAY;AACpC,MAAI,CAAC;AAAqB,UAAA,IAAI,MAAM,uCAAuC;AAErE,QAAA,WAAW,aAAa,IAAI,aAAa;AAE/C,MAAI,CAAC,KAAK,IAAI,aAAa,GAAG;AACvB,SAAA,IAAI,eAAe,CAAA,CAAE;AAAA,EAC5B;AAEM,QAAA,gBAAgB,KAAK,IAAI,aAAa;AACxC,MAAA,YAAY,cAAc,QAAQ;AAE9BC,UAAAA,OAAM,EAAE,SAAS;AACvB,kBAAc,KAAKA,IAAG;AACT,iBAAA,IAAI,eAAe,WAAW,CAAC;AACrCA,WAAAA;AAAAA,EACT;AAEM,QAAA,MAAM,cAAc,QAAQ;AACrB,eAAA,IAAI,eAAe,WAAW,CAAC;AACrC,SAAA;AACT;AAKA,eAAe,SAAS,YAAoB;AACtC,MAAA;AAEI,UAAA,mBAAmB,QAAQ,IAAI,UAAU;AAC/C,QAAI,kBAAkB;AACpB,uBAAiB,QAAQ,CAAU,WAAA;AACjC,YAAI,OAAO;AAAS,iBAAO,QAAQ;AAAA,MAAA,CACpC;AACO,cAAA,IAAI,YAAY,CAAA,CAAE;AAAA,IAC5B;AAEI,QAAA,wBAAwB,mBAAmB,gBAAgB;AACvD,YAAA,qBAAqB,gBAAgB,eAAe;AAAA,IAC5D;AAAA,WACO,OAAO;AACN,YAAA,MAAM,0BAA0B,KAAK;AAAA,EAC/C;AACF;AAGO,SAAS,mBAA+C;AAC7D,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAuB,IAAI;AACrD,SAAO,CAAC,OAAO,MAAM,SAAS,IAAI,CAAC;AACrC;ACnNA,SAAS,IAAI,MAAyB,OAAmB;AACvD,UAAQ,IAAI,kBAAkB,EAAE,MAAM,MAAO,CAAA;AACvC,QAAA,iBAAiB,EAAE,GAAG;AAGxB,MAAA,UAAU,SAAS,GAAG;AACxB,mBAAe,WAAW,MAAM,UAAU,MAAM,KAAK,WAAW,CAAC;AAAA,EACnE;AAEO,SAAA,EAAE,MAAM,OAAO;AACxB;AAEA,MAAM,WAAW,CAAC,EAAE,SAAA,MAAkC;AAEtD,eAAe,cAAc,OAAmC;AFrBzD;AEsBG,UAAA,IAAI,0BAA0B,KAAK;AAG3C,MAAI,SAAS,MAAM;AACV,WAAA,SAAS,eAAe,EAAE;AAAA,EACnC;AAEI,MAAA,OAAO,UAAU,WAAW;AACvB,WAAA,SAAS,eAAe,EAAE;AAAA,EACnC;AAEA,MAAI,OAAO,UAAU,YAAY,OAAO,UAAU,UAAU;AAC1D,WAAO,SAAS,eAAe,OAAO,KAAK,CAAC;AAAA,EAC9C;AAGI,MAAA,MAAM,QAAQ,KAAK,GAAG;AAClB,UAAA,WAAW,SAAS;AAC1B,eAAW,SAAS,OAAO;AACnB,YAAA,OAAO,MAAM,cAAc,KAAK;AACtC,eAAS,YAAY,IAAI;AAAA,IAC3B;AACO,WAAA;AAAA,EACT;AAGA,MAAI,UAAU,SAAS,MAAM,UAAU,QAAW;AAC1C,UAAA,EAAE,MAAM,MAAU,IAAA;AAGpB,QAAA,OAAO,SAAS,YAAY;AAC1B,UAAA;AACF,cAAM,SAAS,MAAM,KAAK,SAAS,CAAE,CAAA;AAC/B,cAAA,OAAO,MAAM,cAAc,MAAM;AACvC,YAAI,gBAAgB,SAAS;AAC3B,eAAK,aAAa,qBAAqB,KAAK,QAAQ,KAAK,UAAU;AAAA,QACrE;AACO,eAAA;AAAA,eACA,OAAO;AACN,gBAAA,MAAM,8BAA8B,KAAK;AAC1C,eAAA,SAAS,eAAe,EAAE;AAAA,MACnC;AAAA,IACF;AAGM,UAAA,UAAU,SAAS,cAAc,IAAc;AAG1C,eAAA,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,SAAS,CAAA,CAAE,GAAG;AACtD,UAAI,QAAQ;AAAY;AACxB,UAAI,IAAI,WAAW,IAAI,KAAK,OAAO,UAAU,YAAY;AACvD,cAAM,YAAY,IAAI,YAAY,EAAE,MAAM,CAAC;AAErC,cAAA,mBAAmB,aAAgB,aAAhB,mBAA2B;AACpD,YAAI,iBAAiB;AACX,kBAAA,oBAAoB,WAAW,eAAe;AAAA,QACxD;AAGQ,gBAAA,iBAAiB,WAAW,KAAsB;AACtD,YAAA,CAAE,QAAgB,UAAU;AAC7B,kBAAgB,WAAW;QAC9B;AACC,gBAAgB,SAAS,SAAS,IAAI;AAAA,MAC9B,WAAA,QAAQ,WAAW,OAAO,UAAU,UAAU;AAChD,eAAA,OAAO,QAAQ,OAAO,KAAK;AAAA,MAAA,WACzB,QAAQ,aAAa;AAC9B,gBAAQ,aAAa,SAAS,OAAO,KAAK,CAAC;AAAA,MAClC,WAAA,QAAQ,SAAS,QAAQ,OAAO;AACzC,gBAAQ,aAAa,KAAK,OAAO,KAAK,CAAC;AAAA,MACzC;AAAA,IACF;AAGA,UAAM,WAAW,+BAAO;AACxB,QAAI,YAAY,MAAM;AACd,YAAA,aAAa,MAAM,QAAQ,QAAQ,IAAI,SAAS,KAAA,IAAS,CAAC,QAAQ;AACxE,iBAAW,SAAS,YAAY;AACxB,cAAA,YAAY,MAAM,cAAc,KAAK;AAC3C,gBAAQ,YAAY,SAAS;AAAA,MAC/B;AAAA,IACF;AAEO,WAAA;AAAA,EACT;AAGA,SAAO,SAAS,eAAe,OAAO,KAAK,CAAC;AAC9C;AClGO,SAAS,cAAiB,cAA6B;AAC5D,QAAM,UAAU;AAAA,IACd,UAAU,CAAC,EAAE,OAAO,eAA6C;AACxD,aAAA;AAAA,IACT;AAAA,IACA,UAAU,CAAC,EAAE,eAAgD;AAC3D,aAAO,SAAS,YAAY;AAAA,IAC9B;AAAA,IACA,KAAK,OAAO;AAAA,IACZ,aAAa,CAAI,aAA8B;AAC7C,aAAO,SAAS,YAAY;AAAA,IAC9B;AAAA,EAAA;AAGK,SAAA;AACT;AAEO,SAAS,WAAc,SAAiB;AACtC,SAAA;AACT;AC3BA,IAAI,cAAc;AAEI,eAAA,QAAQ,SAAc,WAAwB;AACpD,gBAAA;AACV,MAAA;AACI,UAAA,OAAO,SAAS,SAAS;AAAA,EAAA,UAC/B;AACc,kBAAA;AAAA,EAChB;AACF;AAEsB,eAAA,OAAO,SAAc,WAAwB;AACzD,UAAA,IAAI,iBAAiB,UAAU,EAAE;AAEzC,eAAa,YAAY;AACvB,UAAM,aAAa;AACf,QAAA;AACgB,wBAAA,QAAQ,SAAS,SAAS;AACtC,YAAA,UAAU,MAAM,cAAc,OAAO;AAE3C,UAAI,CAAC,aAAa;AAChB,kBAAU,YAAY;AAAA,MACxB;AACA,gBAAU,YAAY,OAAO;AAAA,IAAA,UAE7B;AACa;IACf;AAAA,EAAA,CACD;AACH;ACfgB,SAAA,gBAAgB,SAAiB,SAAkC;AAC3E,QAAA;AAAA,IACJ;AAAA,IACA,cAAc;AAAA,IACd,UAAU,CAAC;AAAA,IACX,SAAS,CAAC;AAAA,IACV,eAAe,CAAC;AAAA,IAChB,OAAO,CAAC;AAAA,IACR,YAAY,CAAC;AAAA,IACb,YAAY,EAAE,MAAM,KAAK;AAAA,EACvB,IAAA;AAEJ,QAAM,iBAAiB,OAAO,QAAQ,SAAS,EAC5C,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,GAAG,GAAG,KAAK,KAAK,GAAG,EACzC,KAAK,GAAG;AAEX,QAAM,iBAAiB,OAAO,QAAQ,SAAS,EAC5C,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,GAAG,GAAG,KAAK,KAAK,GAAG,EACzC,KAAK,GAAG;AAEX,QAAM,WAAW,OAAO,QAAQ,IAAI,EACjC,IAAI,CAAC,CAAC,MAAMC,QAAO,MAAM,mBAAmB,IAAI,cAAcA,QAAO,IAAI,EACzE,KAAK,IAAI;AAEN,QAAA,aAAa,QAAQ,IAAI,CAAU,WAAA;AACnC,QAAA,OAAO,WAAW,UAAU;AAC9B,aAAO,oBAAoB,MAAM;AAAA,IACnC;AACA,UAAM,EAAE,KAAK,QAAQ,MAAM,QAAQ,OAAO,KAAS,IAAA;AACnD,WAAO,oBAAoB,GAAG,IAAI,QAAQ,WAAW,EAAE,GAAG,QAAQ,WAAW,EAAE,GAAG,OAAO,UAAU,IAAI,MAAM,EAAE;AAAA,EAAA,CAChH,EAAE,KAAK,IAAI;AAEL,SAAA;AAAA,QACD,cAAc;AAAA;AAAA;AAAA;AAAA,MAIhB,cAAc,qCAAqC,WAAW;AAAA,IAAS,EAAE,GAAG,QAAQ;AAAA,aAC7E,KAAK;AAAA,EAChB,OAAO,IAAI,CAAQ,SAAA,oCAAoC,IAAI,IAAI,EAAE,KAAK,IAAI,CAAC;AAAA;AAAA,mCAE1C,KAAK,UAAU,YAAY,CAAC;AAAA;AAAA;AAAA,UAGrD,cAAc;AAAA,qBACH,OAAO;AAAA,EAC1B,UAAU;AAAA;AAAA;AAGZ;AC5DA,eAAsB,eACpB,SACA,UAAoC,IACnB;ANVZ;AMcD,MAAA;AAEE,QAAA;AAEA,QAAA,OAAO,YAAY,YAAY;AAEjC,YAAM,WAAW,MAAM,QAAQ,CAAA,CAAE;AACvB,gBAAA,MAAM,mBAAmB,QAAQ;AAAA,IAAA,OACtC;AAEK,gBAAA,MAAM,mBAAmB,OAAO;AAAA,IAC5C;AAEM,UAAA,oBAAmB,aAAQ,YAAR,mBAAiB;AAAA,MAAI,YAC5C,OAAO,WAAW,WACd,EAAE,KAAK,QAAQ,OAAO,KAAK,IAC3B,EAAE,GAAG,QAAQ,OAAO,OAAO,SAAS,KAAK;AAAA;AAI/C,WAAO,gBAAgB,SAAS;AAAA,MAC9B,OAAO;AAAA,MACP,GAAG;AAAA,MACH,SAAS;AAAA,IAAA,CACV;AAAA,WACM,OAAO;AACN,YAAA,MAAM,2BAA2B,KAAK;AAE1C,QAAA,QAAQ,IAAI,aAAa,eAAe;AACpC,YAAA,eAAe,iBAAiB,QAClC,MAAM,SAAS,MAAM,UACrB,OAAO,KAAK;AAET,aAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAMM,WAAW,YAAY,CAAC;AAAA;AAAA;AAAA;AAAA,IAIvC;AACO,WAAA;AAAA,EAAA,UACP;AAAA,EAIF;AACF;AAGA,eAAe,mBAAmB,MAAoC;ANnE/D;AMqED,MAAA,QAAQ,QAAQ,OAAO,SAAS;AAAkB,WAAA;AAGtD,MAAI,QAAQ,OAAO,SAAS,YAAY,OAAO,KAAK,WAAW,YAAY;AACrE,QAAA;AACI,YAAA,SAAS,MAAM,KAAK;AAC1B,aAAO,mBAAmB,MAAM;AAAA,aACzB,OAAO;AACN,cAAA,MAAM,uCAAuC,KAAK;AACnD,aAAA;AAAA,IACT;AAAA,EACF;AAGA,MAAI,OAAO,SAAS,YAAY,OAAO,SAAS,UAAU;AACjD,WAAA,WAAW,OAAO,IAAI,CAAC;AAAA,EAChC;AAGI,MAAA,MAAM,QAAQ,IAAI,GAAG;AACjB,UAAA,UAAU,MAAM,QAAQ;AAAA,MAC5B,KAAK,IAAI,CAAS,UAAA,mBAAmB,KAAK,CAAC;AAAA,IAAA;AAEtC,WAAA,QAAQ,KAAK,EAAE;AAAA,EACxB;AAGI,MAAA,OAAO,SAAS,YAAY;AAEX,KAAA,EAAE,MAAM,KAAK,QAAQ,YAAY;AAGhD,QAAA;AACF,YAAM,SAAS,MAAM,KAAK,CAAA,CAAE;AAC5B,aAAO,mBAAmB,MAAM;AAAA,IAAA,UAChC;AAAA,IAGF;AAAA,EACF;AAGA,MAAI,KAAK,MAAM;AACT,QAAA,OAAO,KAAK,SAAS,YAAY;AAEhB,OAAA;AAAA,QACjB,MAAM,KAAK,KAAK,QAAQ;AAAA,QACxB,OAAO,KAAK,SAAS,CAAC;AAAA,MAAA;AAIpB,UAAA;AACF,cAAM,SAAS,MAAM,KAAK,KAAK,KAAK,SAAS,CAAA,CAAE;AAC/C,eAAO,mBAAmB,MAAM;AAAA,MAAA,UAChC;AAAA,MAGF;AAAA,IACF;AAGI,QAAA,OAAO,IAAI,KAAK,IAAI;AAGxB,QAAI,KAAK,OAAO;AACN,cAAA,YAAY,KAAK,KAAK;AAAA,IAChC;AAGM,UAAA,mCAAmB,IAAI;AAAA,MAC3B;AAAA,MAAQ;AAAA,MAAQ;AAAA,MAAM;AAAA,MAAO;AAAA,MAAS;AAAA,MAAM;AAAA,MAAO;AAAA,MACnD;AAAA,MAAQ;AAAA,MAAQ;AAAA,MAAS;AAAA,MAAU;AAAA,MAAS;AAAA,IAAA,CAC7C;AAED,QAAI,aAAa,IAAI,KAAK,IAAI,GAAG;AAC/B,aAAO,OAAO;AAAA,IAChB;AAEQ,YAAA;AAGJ,SAAA,UAAK,UAAL,mBAAY,UAAU;AACxB,YAAM,WAAW,MAAM,QAAQ,KAAK,MAAM,QAAQ,IAC9C,KAAK,MAAM,WACX,CAAC,KAAK,MAAM,QAAQ;AAExB,iBAAW,SAAS,UAAU;AACpB,gBAAA,MAAM,mBAAmB,KAAK;AAAA,MACxC;AAAA,IACF;AAEO,WAAA,OAAO,KAAK,KAAK,IAAI;AAAA,EAC9B;AAGO,SAAA;AACT;AAEA,SAAS,YAAY,OAAoC;AACvD,MAAI,OAAO;AAEX,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAEhD,QAAI,QAAQ,cAAc,QAAQ,SAAS,QAAQ;AAAO;AAG1D,QAAI,QAAQ,aAAa;AACnB,UAAA;AAAO,gBAAQ,WAAW,WAAW,OAAO,KAAK,CAAC,CAAC;AACvD;AAAA,IACF;AAGA,QAAI,QAAQ,WAAW,OAAO,UAAU,YAAY,UAAU,MAAM;AAC1D,cAAA,WAAW,cAAc,KAAK,CAAC;AACvC;AAAA,IACF;AAGI,QAAA,IAAI,WAAW,IAAI;AAAG;AAGtB,QAAA,OAAO,UAAU,WAAW;AAC1B,UAAA;AAAO,gBAAQ,IAAI,GAAG;AAC1B;AAAA,IACF;AAGA,QAAI,SAAS,MAAM;AACjB,cAAQ,IAAI,GAAG,KAAK,WAAW,OAAO,KAAK,CAAC,CAAC;AAAA,IAC/C;AAAA,EACF;AAEO,SAAA;AACT;AAEA,SAAS,WAAW,KAAqB;AACvC,SAAO,IACJ,QAAQ,MAAM,OAAO,EACrB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,QAAQ,EACtB,QAAQ,MAAM,QAAQ;AAC3B;AAEA,SAAS,cAAc,OAAoC;AACzD,SAAO,OAAO,QAAQ,KAAK,EACxB,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,GAAG,UAAU,GAAG,CAAC,KAAK,KAAK,EAAE,EACnD,KAAK,IAAI;AACd;AAGA,SAAS,UAAU,KAAqB;AACtC,SAAO,IAAI,QAAQ,UAAU,OAAK,MAAM,EAAE,aAAa;AACzD;AChNA,IAAI,eAA4B;AAAA,EAC9B,UAAU,OAAO,WAAW,cAAc,OAAO,SAAS,WAAW;AAAA,EACrE,QAAQ,OAAO,WAAW,cAAc,OAAO,SAAS,SAAS;AAAA,EACjE,MAAM,OAAO,WAAW,cAAc,OAAO,SAAS,OAAO;AAAA,EAC7D,QAAQ,CAAC;AACX;AAEA,MAAM,gCAAgB;AAqBtB,SAAS,YAAY,SAAiB,UAAyB,IAAI;AACjE,QAAM,MAAM,IAAI,IAAI,SAAS,OAAO,SAAS,MAAM;AACnD,QAAM,WAAwB;AAAA,IAC5B,UAAU,IAAI;AAAA,IACd,QAAQ,IAAI;AAAA,IACZ,MAAM,IAAI;AAAA,IACV,QAAQ,CAAC;AAAA,EAAA;AAGX,MAAI,QAAQ,SAAS;AACnB,WAAO,QAAQ,aAAa,UAAU,IAAI,IAAI,UAAU;AAAA,EAAA,OACnD;AACL,WAAO,QAAQ,UAAU,UAAU,IAAI,IAAI,UAAU;AAAA,EACvD;AAEe,iBAAA;AACf,YAAU,QAAQ,CAAA,aAAY,SAAS,QAAQ,CAAC;AAEhD,MAAI,QAAQ,QAAQ;AACX,WAAA,SAAS,GAAG,CAAC;AAAA,EACtB;AACF;AAEO,SAAS,SAAS,IAAY,UAAyB,IAAI;AAChE,MAAI,OAAO,WAAW;AAAa;AACnC,cAAY,IAAI,OAAO;AACzB;AAEO,SAAS,YAAY;AAC1B,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAsB,YAAY;AAE5D,YAAU,MAAM;AACR,UAAA,oBAAoB,CAAC,aAA0B;AACnD,eAAS,QAAQ;AAAA,IAAA;AAGnB,UAAM,iBAAiB,MAAM;AACZ,qBAAA;AAAA,QACb,UAAU,OAAO,SAAS;AAAA,QAC1B,QAAQ,OAAO,SAAS;AAAA,QACxB,MAAM,OAAO,SAAS;AAAA,QACtB,QAAQ,CAAC;AAAA,MAAA;AAEX,eAAS,YAAY;AAAA,IAAA;AAGvB,cAAU,IAAI,iBAAiB;AACxB,WAAA,iBAAiB,YAAY,cAAc;AAElD,WAAO,MAAM;AACX,gBAAU,OAAO,iBAAiB;AAC3B,aAAA,oBAAoB,YAAY,cAAc;AAAA,IAAA;AAAA,EAEzD,GAAG,CAAE,CAAA;AAEE,SAAA;AAAA,IACL,GAAG;AAAA,IACH,MAAM,CAAC,IAAY,YAA4B,SAAS,IAAI,OAAO;AAAA,IACnE,SAAS,CAAC,IAAY,YAA4B,SAAS,IAAI,EAAE,GAAG,SAAS,SAAS,MAAM;AAAA,IAC5F,MAAM,MAAM,OAAO,QAAQ,KAAK;AAAA,IAChC,SAAS,MAAM,OAAO,QAAQ,QAAQ;AAAA,EAAA;AAE1C;AC7FO,SAAS,KAAK;AAAA,EACnB;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAc;AACN,QAAA,cAAc,CAAC,MAAkB;AACrC,MAAE,eAAe;AACR,aAAA,MAAM,EAAE,QAAA,CAAS;AAAA,EAAA;AAGrB,SAAA;AAAA,IACL,MAAM;AAAA,IACN,OAAO;AAAA,MACL;AAAA,MACA,SAAS;AAAA,MACT;AAAA,MACA;AAAA,MACA,GAAG;AAAA,MACH;AAAA,IACF;AAAA,EAAA;AAEJ;"}
1
+ {"version":3,"file":"index.mjs","sources":["../src/batch.ts","../src/hooks.ts","../src/context.ts","../src/jsx-runtime.ts","../src/renderer.ts","../src/server-renderer.ts","../src/router.ts","../src/link.tsx"],"sourcesContent":["export let isBatching = false;\r\nconst queue: Function[] = [];\r\n\r\nexport function batchUpdates(fn: Function) {\r\n if (isBatching) {\r\n queue.push(fn);\r\n return;\r\n }\r\n\r\n isBatching = true;\r\n try {\r\n fn();\r\n while (queue.length > 0) {\r\n const nextFn = queue.shift();\r\n nextFn?.();\r\n }\r\n } finally {\r\n isBatching = false;\r\n }\r\n}\r\n\r\nexport function getIsBatching() {\r\n return isBatching;\r\n}\r\n","import { createElement } from './jsx-runtime';\r\nimport { batchUpdates, isBatching } from './batch';\r\nimport { diff } from './vdom';\r\n\r\nlet currentRender: number = 0;\r\nconst states = new Map<number, any[]>();\r\nconst stateIndices = new Map<number, number>();\r\nconst effects = new Map<number, Effect[]>();\r\nconst memos = new Map<number, { value: any; deps: any[] }[]>();\r\nconst refs = new Map<number, any[]>();\r\n\r\ninterface Effect {\r\n cleanup?: () => void;\r\n deps?: any[];\r\n}\r\n\r\n// Add at the top with other declarations\r\nlet globalRenderCallback: ((element: any, container: HTMLElement) => void) | null = null;\r\nlet globalContainer: HTMLElement | null = null;\r\nlet currentElement: any = null;\r\n\r\nconst isServer = typeof window === 'undefined';\r\nconst serverStates = new Map<number, any>();\r\n\r\nexport function setRenderCallback(\r\n callback: (element: any, container: HTMLElement) => void,\r\n element: any,\r\n container: HTMLElement\r\n) {\r\n globalRenderCallback = callback;\r\n globalContainer = container;\r\n currentElement = element;\r\n}\r\n\r\nexport function prepareRender() {\r\n currentRender++;\r\n stateIndices.set(currentRender, 0);\r\n return currentRender;\r\n}\r\n\r\nexport function finishRender() {\r\n if (isServer) {\r\n serverStates.delete(currentRender);\r\n }\r\n currentRender = 0;\r\n}\r\n\r\nexport function useState<T>(initial: T): [T, (value: T | ((prev: T) => T)) => void] {\r\n if (!currentRender) {\r\n throw new Error('useState must be called within a render');\r\n }\r\n\r\n if (isServer) {\r\n // Server-side state handling\r\n if (!serverStates.has(currentRender)) {\r\n serverStates.set(currentRender, new Map());\r\n }\r\n const componentState = serverStates.get(currentRender)!;\r\n const index = stateIndices.get(currentRender) || 0;\r\n \r\n if (!componentState.has(index)) {\r\n componentState.set(index, initial);\r\n }\r\n\r\n const state = componentState.get(index);\r\n const setState = (newValue: T | ((prev: T) => T)) => {\r\n // No-op for server-side\r\n };\r\n\r\n stateIndices.set(currentRender, index + 1);\r\n return [state, setState];\r\n }\r\n\r\n if (!states.has(currentRender)) {\r\n states.set(currentRender, []);\r\n }\r\n\r\n const componentStates = states.get(currentRender)!;\r\n const index = stateIndices.get(currentRender)!;\r\n \r\n if (index >= componentStates.length) {\r\n componentStates.push(initial);\r\n }\r\n\r\n const state = componentStates[index];\r\n const setState = (newValue: T | ((prev: T) => T)) => {\r\n const nextValue = typeof newValue === 'function' \r\n ? (newValue as Function)(componentStates[index])\r\n : newValue;\r\n\r\n if (componentStates[index] === nextValue) return; // Skip if value hasn't changed\r\n \r\n componentStates[index] = nextValue;\r\n \r\n if (isBatching) {\r\n batchUpdates(() => rerender(currentRender));\r\n } else {\r\n rerender(currentRender);\r\n }\r\n };\r\n\r\n stateIndices.set(currentRender, index + 1);\r\n return [state, setState];\r\n}\r\n\r\nexport function useEffect(callback: () => (() => void) | void, deps?: any[]) {\r\n if (!currentRender) throw new Error('useEffect must be called within a render');\r\n \r\n const effectIndex = stateIndices.get(currentRender)!;\r\n \r\n if (!effects.has(currentRender)) {\r\n effects.set(currentRender, []);\r\n }\r\n\r\n const componentEffects = effects.get(currentRender)!;\r\n const prevEffect = componentEffects[effectIndex];\r\n \r\n // Run effect if deps changed\r\n if (!prevEffect || !deps || !prevEffect.deps || \r\n deps.some((dep, i) => dep !== prevEffect.deps![i])) {\r\n \r\n // Cleanup previous effect\r\n if (prevEffect?.cleanup) {\r\n prevEffect.cleanup();\r\n }\r\n\r\n // Schedule new effect\r\n queueMicrotask(() => {\r\n const cleanup = callback() || undefined;\r\n componentEffects[effectIndex] = { cleanup: cleanup, deps };\r\n });\r\n }\r\n \r\n stateIndices.set(currentRender, effectIndex + 1);\r\n}\r\n\r\nexport function useMemo<T>(factory: () => T, deps: any[]): T {\r\n if (!currentRender) throw new Error('useMemo must be called within a render');\r\n \r\n const memoIndex = stateIndices.get(currentRender)!;\r\n \r\n if (!memos.has(currentRender)) {\r\n memos.set(currentRender, []);\r\n }\r\n\r\n const componentMemos = memos.get(currentRender)!;\r\n const prevMemo = componentMemos[memoIndex];\r\n \r\n if (!prevMemo || (deps && deps.some((dep, i) => !Object.is(dep, prevMemo.deps[i])))) {\r\n const value = factory();\r\n componentMemos[memoIndex] = { value, deps };\r\n stateIndices.set(currentRender, memoIndex + 1);\r\n return value;\r\n }\r\n\r\n stateIndices.set(currentRender, memoIndex + 1);\r\n return prevMemo.value;\r\n}\r\n\r\nexport function useRef<T>(initial: T) {\r\n if (!currentRender) throw new Error('useRef must be called within a render');\r\n \r\n const refIndex = stateIndices.get(currentRender)!;\r\n \r\n if (!refs.has(currentRender)) {\r\n refs.set(currentRender, []);\r\n }\r\n\r\n const componentRefs = refs.get(currentRender)!;\r\n if (refIndex >= componentRefs.length) {\r\n // Initialize with an object that has a current property\r\n const ref = { current: initial };\r\n componentRefs.push(ref);\r\n stateIndices.set(currentRender, refIndex + 1);\r\n return ref;\r\n }\r\n\r\n const ref = componentRefs[refIndex];\r\n stateIndices.set(currentRender, refIndex + 1);\r\n return ref;\r\n}\r\n\r\n// Add a map to track component DOM nodes\r\nconst componentNodes = new Map<Function, Node>();\r\n\r\nasync function rerender(rendererId: number) {\r\n try {\r\n // Clean up effects\r\n const componentEffects = effects.get(rendererId);\r\n if (componentEffects) {\r\n componentEffects.forEach(effect => {\r\n if (effect.cleanup) effect.cleanup();\r\n });\r\n effects.set(rendererId, []);\r\n }\r\n\r\n if (globalRenderCallback && globalContainer && currentElement) {\r\n await globalRenderCallback(currentElement, globalContainer);\r\n }\r\n } catch (error) {\r\n console.error('Error during rerender:', error);\r\n }\r\n}\r\n\r\n// Add new hook for error boundaries\r\nexport function useErrorBoundary(): [Error | null, () => void] {\r\n const [error, setError] = useState<Error | null>(null);\r\n return [error, () => setError(null)];\r\n}\r\n\r\n// Remove withHooks export\r\n","\r\n\r\nconst contexts = new Map<symbol, any>();\r\nlet currentRender: Function | null = null;\r\n\r\nexport interface Context<T> {\r\n Provider: (props: { value: T; children?: any }) => any;\r\n Consumer: (props: { children: (value: T) => any }) => any;\r\n _id: symbol;\r\n useSelector: <S>(selector: (state: T) => S) => S;\r\n}\r\n\r\nexport function createContext<T>(defaultValue: T): Context<T> {\r\n const context = {\r\n Provider: ({ value, children }: { value: T, children?: any }) => {\r\n return children;\r\n },\r\n Consumer: ({ children }: { children: (value: T) => any }) => {\r\n return children(defaultValue);\r\n },\r\n _id: Symbol(),\r\n useSelector: <S>(selector: (state: T) => S) => {\r\n return selector(defaultValue);\r\n }\r\n };\r\n\r\n return context;\r\n}\r\n\r\nexport function useContext<T>(context: any): T {\r\n return context;\r\n}\r\n","import type { Component } from './component';\r\n\r\ninterface VNode {\r\n type: string | Function;\r\n props: Record<string, any>;\r\n}\r\n\r\nfunction jsx(type: string | Function, props: any): VNode {\r\n console.log('JSX Transform:', { type, props });\r\n const processedProps = { ...props };\r\n \r\n // Handle children properly\r\n if (arguments.length > 2) {\r\n processedProps.children = Array.prototype.slice.call(arguments, 2);\r\n }\r\n \r\n return { type, props: processedProps };\r\n}\r\n\r\nconst Fragment = ({ children }: { children: any }) => children;\r\n\r\nasync function createElement(vnode: VNode | any): Promise<Node> {\r\n console.log('Creating element from:', vnode);\r\n\r\n // Handle primitives and null\r\n if (vnode == null) {\r\n return document.createTextNode('');\r\n }\r\n \r\n if (typeof vnode === 'boolean') {\r\n return document.createTextNode('');\r\n }\r\n\r\n if (typeof vnode === 'number' || typeof vnode === 'string') {\r\n return document.createTextNode(String(vnode));\r\n }\r\n\r\n // Handle arrays\r\n if (Array.isArray(vnode)) {\r\n const fragment = document.createDocumentFragment();\r\n for (const child of vnode) {\r\n const node = await createElement(child);\r\n fragment.appendChild(node);\r\n }\r\n return fragment;\r\n }\r\n\r\n // Handle VNode\r\n if ('type' in vnode && vnode.props !== undefined) {\r\n const { type, props } = vnode;\r\n \r\n // Handle function components\r\n if (typeof type === 'function') {\r\n try {\r\n const result = await type(props || {});\r\n const node = await createElement(result);\r\n if (node instanceof Element) {\r\n node.setAttribute('data-component-id', type.name || type.toString());\r\n }\r\n return node;\r\n } catch (error) {\r\n console.error('Error rendering component:', error);\r\n return document.createTextNode('');\r\n }\r\n }\r\n\r\n // Create DOM element\r\n const element = document.createElement(type as string);\r\n \r\n // Handle props\r\n for (const [key, value] of Object.entries(props || {})) {\r\n if (key === 'children') continue;\r\n if (key.startsWith('on') && typeof value === 'function') {\r\n const eventName = key.toLowerCase().slice(2);\r\n // Remove existing event listener if any\r\n const existingHandler = (element as any).__events?.[eventName];\r\n if (existingHandler) {\r\n element.removeEventListener(eventName, existingHandler);\r\n }\r\n \r\n // Add new event listener\r\n element.addEventListener(eventName, value as EventListener);\r\n if (!(element as any).__events) {\r\n (element as any).__events = {};\r\n }\r\n (element as any).__events[eventName] = value;\r\n } else if (key === 'style' && typeof value === 'object') {\r\n Object.assign(element.style, value);\r\n } else if (key === 'className') {\r\n element.setAttribute('class', String(value));\r\n } else if (key !== 'key' && key !== 'ref') {\r\n element.setAttribute(key, String(value));\r\n }\r\n }\r\n\r\n // Handle children\r\n const children = props?.children;\r\n if (children != null) {\r\n const childArray = Array.isArray(children) ? children.flat() : [children];\r\n for (const child of childArray) {\r\n const childNode = await createElement(child);\r\n element.appendChild(childNode);\r\n }\r\n }\r\n\r\n return element;\r\n }\r\n\r\n // Handle other objects by converting to string\r\n return document.createTextNode(String(vnode));\r\n}\r\n\r\n// Export named functions and aliases without duplicates\r\nexport {\r\n jsx,\r\n jsx as jsxs,\r\n jsx as jsxDEV,\r\n Fragment,\r\n createElement\r\n};\r\n\r\n// Named exports object\r\nconst jsxRuntime = {\r\n jsx,\r\n jsxs: jsx,\r\n jsxDEV: jsx,\r\n Fragment,\r\n createElement\r\n};\r\n\r\nexport default jsxRuntime;\r\n","import { createElement } from './jsx-runtime';\r\nimport { prepareRender, finishRender, setRenderCallback } from './hooks';\r\nimport { batchUpdates } from './batch';\r\n\r\nlet isHydrating = false;\r\n\r\nexport async function hydrate(element: any, container: HTMLElement) {\r\n isHydrating = true;\r\n try {\r\n await render(element, container);\r\n } finally {\r\n isHydrating = false;\r\n }\r\n}\r\n\r\nexport async function render(element: any, container: HTMLElement) {\r\n console.log('Rendering to:', container.id);\r\n \r\n batchUpdates(async () => {\r\n const rendererId = prepareRender();\r\n try {\r\n setRenderCallback(render, element, container);\r\n const domNode = await createElement(element);\r\n \r\n if (!isHydrating) {\r\n container.innerHTML = '';\r\n }\r\n container.appendChild(domNode);\r\n \r\n } finally {\r\n finishRender();\r\n }\r\n });\r\n}\r\n","import { prepareRender, finishRender, setRenderCallback } from './hooks';\r\n\r\nexport async function renderToString(element: any): Promise<string> {\r\n // Setup render context\r\n const rendererId = prepareRender();\r\n setRenderCallback(() => {}, element, null as any);\r\n \r\n try {\r\n if (element == null) return '';\r\n \r\n if (typeof element === 'boolean') return '';\r\n \r\n if (typeof element === 'number' || typeof element === 'string') {\r\n return escapeHtml(String(element));\r\n }\r\n\r\n if (Array.isArray(element)) {\r\n const children = await Promise.all(element.map(renderToString));\r\n return children.join('');\r\n }\r\n\r\n if ('type' in element && element.props !== undefined) {\r\n const { type, props } = element;\r\n\r\n // Handle function components\r\n if (typeof type === 'function') {\r\n try {\r\n // Prepare new render context for the component\r\n prepareRender();\r\n const result = await type(props || {});\r\n const html = await renderToString(result);\r\n finishRender();\r\n return html;\r\n } catch (error) {\r\n console.error('Error rendering component:', error);\r\n return '';\r\n }\r\n }\r\n\r\n // Handle regular elements\r\n let html = `<${type}`;\r\n\r\n // Add attributes\r\n for (const [key, value] of Object.entries(props || {})) {\r\n if (key === 'children') continue;\r\n if (key === 'className') {\r\n html += ` class=\"${escapeHtml(String(value))}\"`;\r\n } else if (key === 'style' && typeof value === 'object') {\r\n html += ` style=\"${stringifyStyle(value || {})}\"`;\r\n } else if (!key.startsWith('on')) {\r\n html += ` ${key}=\"${escapeHtml(String(value))}\"`;\r\n }\r\n }\r\n\r\n // Handle self-closing tags\r\n const voidElements = new Set([\r\n 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',\r\n 'link', 'meta', 'param', 'source', 'track', 'wbr'\r\n ]);\r\n \r\n if (voidElements.has(type)) {\r\n return html + '/>';\r\n }\r\n\r\n html += '>';\r\n\r\n // Add children\r\n if (props?.children) {\r\n const children = Array.isArray(props.children) ? props.children : [props.children];\r\n const renderedChildren = await Promise.all(children.map(renderToString));\r\n html += renderedChildren.join('');\r\n }\r\n\r\n return html + `</${type}>`;\r\n }\r\n\r\n return escapeHtml(String(element));\r\n } finally {\r\n finishRender();\r\n }\r\n}\r\n\r\nfunction escapeHtml(str: string): string {\r\n return str\r\n .replace(/&/g, '&amp;')\r\n .replace(/</g, '&lt;')\r\n .replace(/>/g, '&gt;')\r\n .replace(/\"/g, '&quot;')\r\n .replace(/'/g, '&#39;');\r\n}\r\n\r\nfunction stringifyStyle(style: Record<string, any>): string {\r\n return Object.entries(style)\r\n .map(([key, value]) => `${hyphenate(key)}:${value}`)\r\n .join(';');\r\n}\r\n\r\nfunction hyphenate(str: string): string {\r\n return str.replace(/[A-Z]/g, match => '-' + match.toLowerCase());\r\n}\r\n","import { useState, useEffect } from './hooks';\r\n\r\ninterface RouterState {\r\n pathname: string;\r\n search: string;\r\n hash: string;\r\n params: Record<string, string>;\r\n}\r\n\r\ninterface RouterOptions {\r\n replace?: boolean;\r\n scroll?: boolean;\r\n}\r\n\r\nlet currentState: RouterState = {\r\n pathname: typeof window !== 'undefined' ? window.location.pathname : '/',\r\n search: typeof window !== 'undefined' ? window.location.search : '',\r\n hash: typeof window !== 'undefined' ? window.location.hash : '',\r\n params: {}\r\n};\r\n\r\nconst listeners = new Set<(state: RouterState) => void>();\r\n\r\n// Parse route pattern and extract params\r\nfunction matchRoute(pattern: string, pathname: string): Record<string, string> | null {\r\n const paramNames: string[] = [];\r\n const regexPattern = pattern.replace(/:([^/]+)/g, (_, name) => {\r\n paramNames.push(name);\r\n return '([^/]+)';\r\n });\r\n\r\n const match = pathname.match(new RegExp(`^${regexPattern}$`));\r\n if (!match) return null;\r\n\r\n const params: Record<string, string> = {};\r\n paramNames.forEach((name, i) => {\r\n params[name] = match[i + 1];\r\n });\r\n\r\n return params;\r\n}\r\n\r\nfunction updateState(newPath: string, options: RouterOptions = {}) {\r\n const url = new URL(newPath, window.location.origin);\r\n const newState: RouterState = {\r\n pathname: url.pathname,\r\n search: url.search,\r\n hash: url.hash,\r\n params: {}\r\n };\r\n\r\n if (options.replace) {\r\n window.history.replaceState(newState, '', url.toString());\r\n } else {\r\n window.history.pushState(newState, '', url.toString());\r\n }\r\n\r\n currentState = newState;\r\n listeners.forEach(listener => listener(newState));\r\n\r\n if (options.scroll) {\r\n window.scrollTo(0, 0);\r\n }\r\n}\r\n\r\nexport function navigate(to: string, options: RouterOptions = {}) {\r\n if (typeof window === 'undefined') return;\r\n updateState(to, options);\r\n}\r\n\r\nexport function useRouter() {\r\n const [state, setState] = useState<RouterState>(currentState);\r\n\r\n useEffect(() => {\r\n const handleRouteChange = (newState: RouterState) => {\r\n setState(newState);\r\n };\r\n\r\n const handlePopState = () => {\r\n currentState = {\r\n pathname: window.location.pathname,\r\n search: window.location.search,\r\n hash: window.location.hash,\r\n params: {}\r\n };\r\n setState(currentState);\r\n };\r\n\r\n listeners.add(handleRouteChange);\r\n window.addEventListener('popstate', handlePopState);\r\n\r\n return () => {\r\n listeners.delete(handleRouteChange);\r\n window.removeEventListener('popstate', handlePopState);\r\n };\r\n }, []);\r\n\r\n return {\r\n ...state,\r\n push: (to: string, options?: RouterOptions) => navigate(to, options),\r\n replace: (to: string, options?: RouterOptions) => navigate(to, { ...options, replace: true }),\r\n back: () => window.history.back(),\r\n forward: () => window.history.forward()\r\n };\r\n}\r\n\r\nexport function useRoute(pattern: string) {\r\n const { pathname } = useRouter();\r\n const [params, setParams] = useState<Record<string, string>>({});\r\n const [isMatch, setIsMatch] = useState(false);\r\n\r\n useEffect(() => {\r\n const matchedParams = matchRoute(pattern, pathname);\r\n setIsMatch(!!matchedParams);\r\n setParams(matchedParams || {});\r\n }, [pattern, pathname]);\r\n\r\n return { isMatch, params };\r\n}\r\n\r\nexport function useParams<T extends Record<string, string>>() {\r\n const router = useRouter();\r\n return router.params as T;\r\n}\r\n\r\nexport function useSearchParams() {\r\n const { search } = useRouter();\r\n return new URLSearchParams(search);\r\n}\r\n","import { navigate } from './router';\r\n\r\ninterface LinkProps {\r\n href: string;\r\n children?: any;\r\n replace?: boolean;\r\n className?: string;\r\n style?: Record<string, string>;\r\n [key: string]: any;\r\n}\r\n\r\nexport function Link({ \r\n href, \r\n children, \r\n replace = false, \r\n className,\r\n style,\r\n ...rest \r\n}: LinkProps) {\r\n const handleClick = (e: MouseEvent) => {\r\n e.preventDefault();\r\n navigate(href, { replace });\r\n };\r\n\r\n return {\r\n type: 'a',\r\n props: {\r\n href,\r\n onClick: handleClick,\r\n className,\r\n style,\r\n ...rest,\r\n children\r\n }\r\n };\r\n}\r\n"],"names":["index","state","setState","ref","html"],"mappings":"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"}
@@ -1,2 +1 @@
1
- import { TemplateOptions } from './templates/default';
2
- export declare function renderToString(element: JSX.Element | any, options?: Partial<TemplateOptions>): Promise<string>;
1
+ export declare function renderToString(element: any): Promise<string>;
@@ -1,13 +1,7 @@
1
- export interface ScriptConfig {
2
- src: string;
3
- defer?: boolean;
4
- async?: boolean;
5
- type?: string;
6
- }
7
1
  export interface TemplateOptions {
8
2
  title: string;
9
3
  description?: string;
10
- scripts?: (string | ScriptConfig)[];
4
+ scripts?: string[];
11
5
  styles?: string[];
12
6
  initialState?: any;
13
7
  meta?: Record<string, string>;
package/dist/types.d.ts CHANGED
@@ -1,12 +1,15 @@
1
1
  export type { Context } from './context';
2
2
  export interface VNode {
3
- type: string | Function | ((props: any) => any);
3
+ type: string | Function;
4
4
  props: Record<string, any>;
5
- key?: string | null;
5
+ key?: string | number;
6
6
  }
7
7
  declare global {
8
8
  namespace JSX {
9
- interface Element extends VNode {
9
+ interface Element {
10
+ type: string | Function;
11
+ props: Record<string, any>;
12
+ key?: string | number;
10
13
  }
11
14
  interface IntrinsicElements {
12
15
  [elemName: string]: any;
package/package.json CHANGED
@@ -1,7 +1,6 @@
1
-
2
1
  {
3
2
  "name": "frontend-hamroun",
4
- "version": "1.1.54",
3
+ "version": "1.1.56",
5
4
  "description": "A lightweight frontend framework with hooks and virtual DOM",
6
5
  "type": "module",
7
6
  "main": "./dist/index.js",
@@ -2,17 +2,14 @@ import { render, useState, Link, useRouter } from 'frontend-hamroun';
2
2
 
3
3
  // Home component
4
4
  function Home() {
5
- const [count, setCount] = useState<number>(0);
6
-
7
- const displayCount = typeof count === 'number' ? count : 0;
8
-
5
+ const [count, setCount] = useState(0);
9
6
  return (
10
7
  <div>
11
8
  <h1>Home Page</h1>
12
9
  <div>
13
- <button onClick={() => setCount((prev) => (typeof prev === 'number' ? prev - 1 : 0))}>-</button>
14
- <span style={{ margin: '0 10px' }}>{displayCount}</span>
15
- <button onClick={() => setCount((prev) => (typeof prev === 'number' ? prev + 1 : 0))}>+</button>
10
+ <button onClick={() => setCount(count - 1)}>-</button>
11
+ <span style={{ margin: '0 10px' }}>{count}</span>
12
+ <button onClick={() => setCount(count + 1)}>+</button>
16
13
  </div>
17
14
  </div>
18
15
  );
@@ -45,8 +42,8 @@ function App() {
45
42
  return (
46
43
  <div>
47
44
  <Navigation />
48
- {router.pathname === '/' ? <Home /> : null}
49
- {router.pathname === '/about' ? <About /> : null}
45
+ {router.pathname === '/' && <Home />}
46
+ {router.pathname === '/about' && <About />}
50
47
  </div>
51
48
  );
52
49
  }
@@ -1,15 +1,14 @@
1
1
  {
2
- "name": "ssr-template",
2
+ "name": "ssr-version",
3
3
  "private": true,
4
4
  "version": "0.0.0",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "dev": "vite",
8
- "build": "vite build",
9
- "build:client": "vite build --outDir dist/client",
10
- "build:server": "vite build --ssr src/server.ts --outDir dist/server",
11
- "start": "node dist/server/server.js",
12
- "preview": "vite preview"
8
+ "build:client": "vite build",
9
+ "build:server": "vite build --ssr",
10
+ "build": "npm run build:client && npm run build:server",
11
+ "start": "node dist/server.js"
13
12
  },
14
13
  "dependencies": {
15
14
  "express": "^4.18.2",
@@ -1,122 +1,26 @@
1
- import { useState, useEffect, Link, useRouter, createContext, useContext } from 'frontend-hamroun';
1
+ import { useState } from 'frontend-hamroun';
2
2
 
3
- interface AppProps {
4
- url: string;
5
- user: any | null;
6
- settings: {
7
- theme: string;
8
- };
9
- }
10
-
11
- // Create theme context
12
- const ThemeContext = createContext<{
13
- theme: string;
14
- setTheme: (theme: string) => void;
15
- }>({ theme: 'light', setTheme: () => {} });
16
-
17
- // Layout component
18
- function Layout({ children }: { children: any }) {
19
- const { theme, setTheme } = useContext(ThemeContext);
20
- const router = useRouter();
3
+ export function App() {
4
+ // Initialize with a default state that works on both server and client
5
+ const [count, setCount] = useState(0);
21
6
 
22
7
  return (
23
- <div className="app" data-theme={theme}>
24
- <header>
25
- <nav>
26
- <Link href="/">Home</Link>
27
- <Link href="/about">About</Link>
28
- <Link href="/users">Users</Link>
29
- <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
30
- {theme === 'light' ? '🌙' : '☀️'}
31
- </button>
32
- </nav>
33
- </header>
34
- <main>
35
- {children}
36
- </main>
37
- <footer>
38
- <p>Current route: {router.pathname}</p>
39
- </footer>
8
+ <div>
9
+ <h1>Server-Side Rendered App</h1>
10
+ <div>
11
+ <button
12
+ onClick={() => setCount(count - 1)}
13
+ data-action="decrement"
14
+ >-</button>
15
+ <span style={{ margin: '0 10px' }}>{count}</span>
16
+ <button
17
+ onClick={() => setCount(count + 1)}
18
+ data-action="increment"
19
+ >+</button>
20
+ </div>
21
+ <script dangerouslySetInnerHTML={{
22
+ __html: `window.__INITIAL_STATE__ = ${JSON.stringify({ count: 0 })};`
23
+ }} />
40
24
  </div>
41
25
  );
42
26
  }
43
-
44
- export function App({ url, user, settings }: AppProps) {
45
- const [theme, setTheme] = useState(settings.theme);
46
- const router = useRouter();
47
-
48
- useEffect(() => {
49
- document.documentElement.dataset.theme = theme;
50
- }, [theme]);
51
-
52
- return (
53
- <ThemeContext.Provider value={{ theme, setTheme }}>
54
- <Layout>
55
- {router.pathname === '/' && (
56
- <section>
57
- <h1>Welcome</h1>
58
- {user ? (
59
- <p>Hello, {user.name}!</p>
60
- ) : (
61
- <p>Welcome, guest!</p>
62
- )}
63
- </section>
64
- )}
65
-
66
- {router.pathname === '/about' && (
67
- <section>
68
- <h1>About</h1>
69
- <p>This is a server-side rendered app.</p>
70
- </section>
71
- )}
72
-
73
- {router.pathname === '/users' && (
74
- <section>
75
- <h1>Users</h1>
76
- <UserList />
77
- </section>
78
- )}
79
- </Layout>
80
- </ThemeContext.Provider>
81
- );
82
- }
83
-
84
- // Add new UserList component
85
- function UserList() {
86
- const [users, setUsers] = useState<any[]>([]);
87
- const [loading, setLoading] = useState(true);
88
-
89
- useEffect(() => {
90
- // This will only execute on the client
91
- if (typeof window !== 'undefined') {
92
- fetch('https://jsonplaceholder.typicode.com/users')
93
- .then(res => res.json())
94
- .then(data => {
95
- setUsers(data);
96
- setLoading(false);
97
- })
98
- .catch(error => {
99
- console.error('Error fetching users:', error);
100
- setLoading(false);
101
- });
102
- }
103
- }, []);
104
-
105
- // Always show loading indicator on server
106
- if (typeof window === 'undefined') {
107
- return <div>Loading users...</div>;
108
- }
109
-
110
- if (loading) return <div>Loading users...</div>;
111
-
112
- return (
113
- <ul>
114
- {users.map(user => (
115
- <li key={user.id}>
116
- <h3>{user.name}</h3>
117
- <p>{user.email}</p>
118
- </li>
119
- ))}
120
- </ul>
121
- );
122
- }
@@ -1,41 +1,11 @@
1
- import { hydrate, createElement } from 'frontend-hamroun';
1
+ import { hydrate } from 'frontend-hamroun';
2
2
  import { App } from './App';
3
3
 
4
- // Type-safe initial state
5
- interface InitialState {
6
- url: string;
7
- user: null | { id: number; name: string };
8
- settings: {
9
- theme: 'light' | 'dark';
10
- };
11
- }
12
4
 
13
- declare global {
14
- interface Window {
15
- __INITIAL_STATE__: InitialState;
16
- }
17
- }
18
5
 
19
- // Get state injected by SSR
20
- const initialState = window.__INITIAL_STATE__ || {
21
- url: window.location.pathname,
22
- user: null,
23
- settings: { theme: 'light' }
24
- };
25
-
26
- // Use createElement for explicit type safety and better error handling
27
6
  document.addEventListener('DOMContentLoaded', () => {
28
- const appElement = createElement(App, {
29
- url: initialState.url,
30
- user: initialState.user,
31
- settings: initialState.settings
32
- });
33
-
34
7
  hydrate(
35
- appElement,
8
+ <App />,
36
9
  document.getElementById('root')!
37
10
  );
38
-
39
- // Clean up after hydration
40
- delete window.__INITIAL_STATE__;
41
11
  });
@@ -1,86 +1,46 @@
1
1
  import express from 'express';
2
2
  import path from 'path';
3
3
  import { fileURLToPath } from 'url';
4
- import { renderToString, createElement, jsx } from 'frontend-hamroun';
5
- import { App } from './App';
4
+ import { renderToString, jsx } from 'frontend-hamroun';
5
+ import { App } from './App.js';
6
+ import fs from 'fs';
6
7
 
7
8
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
8
9
  const app = express();
10
+ const port = 3000;
9
11
 
10
- // Static files
11
- app.use(express.static(path.join(__dirname, 'dist')));
12
-
13
- // Add API endpoints
14
- app.get('/api/users', async (_req: any, res: { json: (arg0: any) => void; status: (arg0: number) => { (): any; new(): any; json: { (arg0: { error: string; }): void; new(): any; }; }; }) => {
15
- try {
16
- const response = await fetch('https://jsonplaceholder.typicode.com/users');
17
- const users = await response.json();
18
- res.json(users);
19
- } catch (error) {
20
- res.status(500).json({ error: 'Failed to fetch users' });
21
- }
22
- });
12
+ // Find the client entry file
13
+ const getClientEntry = () => {
14
+ const assetsDir = path.join(__dirname, './');
15
+ const files = fs.readdirSync(assetsDir);
16
+ return files.find(file => file.startsWith("client")&& file.endsWith('.js'));
17
+ };
23
18
 
24
- app.get('*', async (req, res) => {
25
- try {
26
- // Get initial state based on route
27
- const initialState = {
28
- url: req.url,
29
- user: null,
30
- settings: { theme: 'light' }
31
- };
19
+ // Serve static files from dist/assets
20
+ app.use('/assets', express.static(path.join(__dirname, './')));
32
21
 
33
- // Add dynamic meta tags based on route
34
- const meta: Record<string, string> = {
35
- 'viewport': 'width=device-width, initial-scale=1.0',
36
- 'theme-color': '#ffffff',
37
- };
38
-
39
- if (req.url === '/users') {
40
- meta['description'] = 'View all users';
41
- meta['og:title'] = 'Users List';
42
- }
43
-
44
- // Create element explicitly for better SSR compatibility
45
- // This works better with TypeScript and JSX transformation
46
- const appElement = jsx(App, {
47
- url: req.url,
48
- user: initialState.user,
49
- settings: initialState.settings
50
- });
22
+ // Serve static files from dist
23
+ app.use(express.static(path.join(__dirname, 'dist')));
51
24
 
52
- const html = await renderToString(appElement, {
53
- title: `SSR Demo${req.url === '/' ? '' : ` - ${req.url.slice(1)}`}`,
54
- description: 'Server-side rendered application',
55
- scripts: ['/client.js'], // Use path to built client code
56
- styles: ['/styles.css'],
57
- initialState,
58
- meta,
59
- bodyAttrs: {
60
- 'data-theme': initialState.settings.theme,
61
- 'data-route': req.url
62
- }
63
- });
64
-
65
- res.send(html);
66
- } catch (error) {
67
- console.error('SSR Error:', error);
68
- // Fallback error page
69
- res.status(500).send(`
70
- <!DOCTYPE html>
71
- <html>
72
- <head>
73
- <title>Error</title>
74
- </head>
75
- <body>
76
- <h1>500 - Server Error</h1>
77
- <script>window.location.reload()</script>
78
- </body>
79
- </html>
80
- `);
81
- }
25
+ app.get('/', async (_req: any, res: { send: (arg0: string) => void; }) => {
26
+ const clientEntry = getClientEntry();
27
+ const html = await renderToString(jsx(App, null
28
+ ));
29
+
30
+ res.send(`
31
+ <!DOCTYPE html>
32
+ <html>
33
+ <head>
34
+ <title>SSR App</title>
35
+ </head>
36
+ <body>
37
+ <div id="root">${html}</div>
38
+ <script type="module" src="/assets/${clientEntry}"></script>
39
+ </body>
40
+ </html>
41
+ `);
82
42
  });
83
43
 
84
- app.listen(3000, () => {
85
- console.log('✓ SSR Server running at http://localhost:3000');
44
+ app.listen(port, () => {
45
+ console.log(`Server running at http://localhost:${port}`);
86
46
  });
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "compilerOptions": {
3
- "target": "ESNext",
3
+ "target": "ES2020",
4
4
  "module": "ESNext",
5
5
  "lib": ["ES2020", "DOM", "DOM.Iterable"],
6
- "moduleResolution": "Node",
6
+ "moduleResolution": "bundler",
7
7
  "jsx": "preserve",
8
8
  "jsxFactory": "jsx",
9
9
  "jsxFragmentFactory": "Fragment",
@@ -15,11 +15,9 @@
15
15
  "resolveJsonModule": true,
16
16
  "isolatedModules": true,
17
17
  "noEmit": true,
18
- "types": ["vite/client"],
19
- "outDir": "dist",
20
- "baseUrl": ".",
18
+ "types": ["node"],
21
19
  "paths": {
22
- "frontend-hamroun": ["node_modules/frontend-hamroun/dist/index.d.ts"]
20
+ "frontend-hamroun": ["./node_modules/frontend-hamroun"]
23
21
  }
24
22
  },
25
23
  "include": ["src"],