frontend-hamroun 1.2.25 → 1.2.27
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/index.client.d.ts +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +26 -1
- package/templates/fullstack-app/server.js +147 -5
- package/templates/ssr-template/readme.md +112 -15
- package/templates/ssr-template/server.ts +166 -14
package/dist/index.client.d.ts
CHANGED
@@ -6,6 +6,7 @@ export { render, hydrate } from './renderer';
|
|
6
6
|
export { renderToString } from './server-renderer';
|
7
7
|
export type { Context } from './context';
|
8
8
|
export type { VNode } from './types';
|
9
|
+
export type { Server, ServerConfig, User, DbConfig, MiddlewareFunction } from './server-types';
|
9
10
|
export declare const server: {
|
10
11
|
getServer(): Promise<never>;
|
11
12
|
};
|
package/dist/index.d.ts
CHANGED
@@ -8,5 +8,5 @@ export type { Context } from './context';
|
|
8
8
|
export type { VNode } from './types';
|
9
9
|
export type { Server, ServerConfig, User, DbConfig, MiddlewareFunction } from './server-types';
|
10
10
|
export declare const server: {
|
11
|
-
getServer(): Promise<
|
11
|
+
getServer(): Promise<typeof import("./server/index.js")>;
|
12
12
|
};
|
package/dist/index.js.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/batch.ts","../src/hooks.ts","../src/context.ts","../src/jsx-runtime.ts","../src/renderer.ts","../src/server-renderer.ts","../src/index.client.ts"],"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 { VNode, Component } from './types';\r\nimport { prepareRender, finishRender } from './hooks';\r\n\r\n/**\r\n * Renders a virtual DOM tree to an HTML string\r\n */\r\nexport function renderToString(vnode: VNode): string {\r\n // Reset hook state for this render\r\n prepareRender();\r\n \r\n // Render the tree to HTML\r\n const html = renderNodeToString(vnode);\r\n \r\n // Clean up after rendering\r\n finishRender();\r\n \r\n return html;\r\n}\r\n\r\n/**\r\n * Internal function to convert a virtual node to an HTML string\r\n */\r\nfunction renderNodeToString(vnode: VNode | string | number | boolean | null | undefined): string {\r\n // Handle primitive values\r\n if (vnode === null || vnode === undefined) return '';\r\n if (typeof vnode === 'boolean') return '';\r\n if (typeof vnode === 'number' || typeof vnode === 'string') return escapeHtml(String(vnode));\r\n \r\n // Handle function components\r\n if (typeof vnode.type === 'function') {\r\n const Component = vnode.type as Component;\r\n const renderedNode = Component(vnode.props || {});\r\n return renderNodeToString(renderedNode);\r\n }\r\n \r\n // Handle intrinsic elements (regular HTML tags)\r\n if (typeof vnode.type === 'string') {\r\n const tag = vnode.type;\r\n let attrs = '';\r\n let children = '';\r\n \r\n // Convert props to HTML attributes\r\n if (vnode.props) {\r\n for (const [key, value] of Object.entries(vnode.props)) {\r\n // Skip children prop as we handle it separately\r\n if (key === 'children') continue;\r\n \r\n // Skip event handlers (they start with 'on')\r\n if (key.startsWith('on')) continue;\r\n \r\n // Handle the className prop specially\r\n if (key === 'className') {\r\n attrs += ` class=\"${escapeHtml(value as string)}\"`;\r\n continue;\r\n }\r\n \r\n // Handle other attributes\r\n if (typeof value === 'string' || typeof value === 'number') {\r\n attrs += ` ${key}=\"${escapeHtml(String(value))}\"`;\r\n } else if (value === true) {\r\n // Boolean attributes\r\n attrs += ` ${key}`;\r\n }\r\n }\r\n }\r\n \r\n // Process children\r\n const childrenArray = (vnode.props?.children) \r\n ? Array.isArray(vnode.props.children) \r\n ? vnode.props.children \r\n : [vnode.props.children]\r\n : [];\r\n \r\n for (const child of childrenArray) {\r\n children += renderNodeToString(child);\r\n }\r\n \r\n // Self-closing tags\r\n const selfClosing = [\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 (selfClosing.includes(tag)) {\r\n return `<${tag}${attrs}/>`;\r\n }\r\n \r\n // Regular tags with closing\r\n return `<${tag}${attrs}>${children}</${tag}>`;\r\n }\r\n \r\n // Handle fragments\r\n if (vnode.type === Symbol.for('react.fragment')) {\r\n let fragmentOutput = '';\r\n const children = Array.isArray(vnode.props?.children)\r\n ? vnode.props.children\r\n : vnode.props?.children ? [vnode.props.children] : [];\r\n \r\n for (const child of children) {\r\n fragmentOutput += renderNodeToString(child);\r\n }\r\n \r\n return fragmentOutput;\r\n }\r\n \r\n // Fallback for unknown node types\r\n console.warn('Unknown vnode type:', vnode.type);\r\n return '';\r\n}\r\n\r\n/**\r\n * Escape HTML special characters to prevent XSS\r\n */\r\nfunction escapeHtml(text: string): string {\r\n return text\r\n .replace(/&/g, '&')\r\n .replace(/</g, '<')\r\n .replace(/>/g, '>')\r\n .replace(/\"/g, '"')\r\n .replace(/'/g, ''');\r\n}\r\n","import { createElement } from './jsx-runtime';\r\nimport { prepareRender, finishRender, setRenderCallback } from './hooks';\r\n\r\nexport { \r\n useState, \r\n useEffect, \r\n useMemo, \r\n useRef,\r\n useErrorBoundary \r\n} from './hooks';\r\n\r\nexport { createContext, useContext } from './context';\r\nexport { batchUpdates } from './batch';\r\nexport { jsx, jsxs, Fragment } from './jsx-runtime';\r\nexport { render, hydrate } from './renderer';\r\nexport { renderToString } from './server-renderer';\r\n\r\n// Re-export types for client-side usage\r\nexport type { Context } from './context';\r\nexport type { VNode } from './types';\r\n\r\n// Export a placeholder for server functionality that works in browser environments\r\nexport const server = {\r\n async getServer() {\r\n throw new Error('Server module can only be used in Node.js environment');\r\n }\r\n};\r\n\r\nlet isHydrating = false;\r\n"],"names":["index","state","setState","ref","Component"],"mappings":"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"}
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/batch.ts","../src/hooks.ts","../src/context.ts","../src/jsx-runtime.ts","../src/renderer.ts","../src/server-renderer.ts","../src/index.client.ts"],"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 { VNode, Component } from './types';\r\nimport { prepareRender, finishRender } from './hooks';\r\n\r\n/**\r\n * Renders a virtual DOM tree to an HTML string\r\n */\r\nexport function renderToString(vnode: VNode): string {\r\n // Reset hook state for this render\r\n prepareRender();\r\n \r\n // Render the tree to HTML\r\n const html = renderNodeToString(vnode);\r\n \r\n // Clean up after rendering\r\n finishRender();\r\n \r\n return html;\r\n}\r\n\r\n/**\r\n * Internal function to convert a virtual node to an HTML string\r\n */\r\nfunction renderNodeToString(vnode: VNode | string | number | boolean | null | undefined): string {\r\n // Handle primitive values\r\n if (vnode === null || vnode === undefined) return '';\r\n if (typeof vnode === 'boolean') return '';\r\n if (typeof vnode === 'number' || typeof vnode === 'string') return escapeHtml(String(vnode));\r\n \r\n // Handle function components\r\n if (typeof vnode.type === 'function') {\r\n const Component = vnode.type as Component;\r\n const renderedNode = Component(vnode.props || {});\r\n return renderNodeToString(renderedNode);\r\n }\r\n \r\n // Handle intrinsic elements (regular HTML tags)\r\n if (typeof vnode.type === 'string') {\r\n const tag = vnode.type;\r\n let attrs = '';\r\n let children = '';\r\n \r\n // Convert props to HTML attributes\r\n if (vnode.props) {\r\n for (const [key, value] of Object.entries(vnode.props)) {\r\n // Skip children prop as we handle it separately\r\n if (key === 'children') continue;\r\n \r\n // Skip event handlers (they start with 'on')\r\n if (key.startsWith('on')) continue;\r\n \r\n // Handle the className prop specially\r\n if (key === 'className') {\r\n attrs += ` class=\"${escapeHtml(value as string)}\"`;\r\n continue;\r\n }\r\n \r\n // Handle other attributes\r\n if (typeof value === 'string' || typeof value === 'number') {\r\n attrs += ` ${key}=\"${escapeHtml(String(value))}\"`;\r\n } else if (value === true) {\r\n // Boolean attributes\r\n attrs += ` ${key}`;\r\n }\r\n }\r\n }\r\n \r\n // Process children\r\n const childrenArray = (vnode.props?.children) \r\n ? Array.isArray(vnode.props.children) \r\n ? vnode.props.children \r\n : [vnode.props.children]\r\n : [];\r\n \r\n for (const child of childrenArray) {\r\n children += renderNodeToString(child);\r\n }\r\n \r\n // Self-closing tags\r\n const selfClosing = [\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 (selfClosing.includes(tag)) {\r\n return `<${tag}${attrs}/>`;\r\n }\r\n \r\n // Regular tags with closing\r\n return `<${tag}${attrs}>${children}</${tag}>`;\r\n }\r\n \r\n // Handle fragments\r\n if (vnode.type === Symbol.for('react.fragment')) {\r\n let fragmentOutput = '';\r\n const children = Array.isArray(vnode.props?.children)\r\n ? vnode.props.children\r\n : vnode.props?.children ? [vnode.props.children] : [];\r\n \r\n for (const child of children) {\r\n fragmentOutput += renderNodeToString(child);\r\n }\r\n \r\n return fragmentOutput;\r\n }\r\n \r\n // Fallback for unknown node types\r\n console.warn('Unknown vnode type:', vnode.type);\r\n return '';\r\n}\r\n\r\n/**\r\n * Escape HTML special characters to prevent XSS\r\n */\r\nfunction escapeHtml(text: string): string {\r\n return text\r\n .replace(/&/g, '&')\r\n .replace(/</g, '<')\r\n .replace(/>/g, '>')\r\n .replace(/\"/g, '"')\r\n .replace(/'/g, ''');\r\n}\r\n","import { createElement } from './jsx-runtime';\r\nimport { prepareRender, finishRender, setRenderCallback } from './hooks';\r\n\r\nexport { \r\n useState, \r\n useEffect, \r\n useMemo, \r\n useRef,\r\n useErrorBoundary \r\n} from './hooks';\r\n\r\nexport { createContext, useContext } from './context';\r\nexport { batchUpdates } from './batch';\r\nexport { jsx, jsxs, Fragment } from './jsx-runtime';\r\nexport { render, hydrate } from './renderer';\r\nexport { renderToString } from './server-renderer';\r\n\r\n// Re-export types for client-side usage\r\nexport type { Context } from './context';\r\nexport type { VNode } from './types';\r\n\r\n// Export server types for type checking\r\nexport type {\r\n Server,\r\n ServerConfig,\r\n User,\r\n DbConfig,\r\n MiddlewareFunction\r\n} from './server-types';\r\n\r\n// Provide placeholder server functionality\r\nexport const server = {\r\n async getServer() {\r\n throw new Error('Server module can only be used in Node.js environment');\r\n }\r\n};\r\n\r\nlet isHydrating = false;\r\n"],"names":["index","state","setState","ref","Component"],"mappings":"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"}
|
package/dist/index.mjs.map
CHANGED
@@ -1 +1 @@
|
|
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/index.client.ts"],"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 { VNode, Component } from './types';\r\nimport { prepareRender, finishRender } from './hooks';\r\n\r\n/**\r\n * Renders a virtual DOM tree to an HTML string\r\n */\r\nexport function renderToString(vnode: VNode): string {\r\n // Reset hook state for this render\r\n prepareRender();\r\n \r\n // Render the tree to HTML\r\n const html = renderNodeToString(vnode);\r\n \r\n // Clean up after rendering\r\n finishRender();\r\n \r\n return html;\r\n}\r\n\r\n/**\r\n * Internal function to convert a virtual node to an HTML string\r\n */\r\nfunction renderNodeToString(vnode: VNode | string | number | boolean | null | undefined): string {\r\n // Handle primitive values\r\n if (vnode === null || vnode === undefined) return '';\r\n if (typeof vnode === 'boolean') return '';\r\n if (typeof vnode === 'number' || typeof vnode === 'string') return escapeHtml(String(vnode));\r\n \r\n // Handle function components\r\n if (typeof vnode.type === 'function') {\r\n const Component = vnode.type as Component;\r\n const renderedNode = Component(vnode.props || {});\r\n return renderNodeToString(renderedNode);\r\n }\r\n \r\n // Handle intrinsic elements (regular HTML tags)\r\n if (typeof vnode.type === 'string') {\r\n const tag = vnode.type;\r\n let attrs = '';\r\n let children = '';\r\n \r\n // Convert props to HTML attributes\r\n if (vnode.props) {\r\n for (const [key, value] of Object.entries(vnode.props)) {\r\n // Skip children prop as we handle it separately\r\n if (key === 'children') continue;\r\n \r\n // Skip event handlers (they start with 'on')\r\n if (key.startsWith('on')) continue;\r\n \r\n // Handle the className prop specially\r\n if (key === 'className') {\r\n attrs += ` class=\"${escapeHtml(value as string)}\"`;\r\n continue;\r\n }\r\n \r\n // Handle other attributes\r\n if (typeof value === 'string' || typeof value === 'number') {\r\n attrs += ` ${key}=\"${escapeHtml(String(value))}\"`;\r\n } else if (value === true) {\r\n // Boolean attributes\r\n attrs += ` ${key}`;\r\n }\r\n }\r\n }\r\n \r\n // Process children\r\n const childrenArray = (vnode.props?.children) \r\n ? Array.isArray(vnode.props.children) \r\n ? vnode.props.children \r\n : [vnode.props.children]\r\n : [];\r\n \r\n for (const child of childrenArray) {\r\n children += renderNodeToString(child);\r\n }\r\n \r\n // Self-closing tags\r\n const selfClosing = [\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 (selfClosing.includes(tag)) {\r\n return `<${tag}${attrs}/>`;\r\n }\r\n \r\n // Regular tags with closing\r\n return `<${tag}${attrs}>${children}</${tag}>`;\r\n }\r\n \r\n // Handle fragments\r\n if (vnode.type === Symbol.for('react.fragment')) {\r\n let fragmentOutput = '';\r\n const children = Array.isArray(vnode.props?.children)\r\n ? vnode.props.children\r\n : vnode.props?.children ? [vnode.props.children] : [];\r\n \r\n for (const child of children) {\r\n fragmentOutput += renderNodeToString(child);\r\n }\r\n \r\n return fragmentOutput;\r\n }\r\n \r\n // Fallback for unknown node types\r\n console.warn('Unknown vnode type:', vnode.type);\r\n return '';\r\n}\r\n\r\n/**\r\n * Escape HTML special characters to prevent XSS\r\n */\r\nfunction escapeHtml(text: string): string {\r\n return text\r\n .replace(/&/g, '&')\r\n .replace(/</g, '<')\r\n .replace(/>/g, '>')\r\n .replace(/\"/g, '"')\r\n .replace(/'/g, ''');\r\n}\r\n","import { createElement } from './jsx-runtime';\r\nimport { prepareRender, finishRender, setRenderCallback } from './hooks';\r\n\r\nexport { \r\n useState, \r\n useEffect, \r\n useMemo, \r\n useRef,\r\n useErrorBoundary \r\n} from './hooks';\r\n\r\nexport { createContext, useContext } from './context';\r\nexport { batchUpdates } from './batch';\r\nexport { jsx, jsxs, Fragment } from './jsx-runtime';\r\nexport { render, hydrate } from './renderer';\r\nexport { renderToString } from './server-renderer';\r\n\r\n// Re-export types for client-side usage\r\nexport type { Context } from './context';\r\nexport type { VNode } from './types';\r\n\r\n// Export a placeholder for server functionality that works in browser environments\r\nexport const server = {\r\n async getServer() {\r\n throw new Error('Server module can only be used in Node.js environment');\r\n }\r\n};\r\n\r\nlet isHydrating = false;\r\n"],"names":["index","state","setState","ref","Component"],"mappings":"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"}
|
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/index.client.ts"],"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 { VNode, Component } from './types';\r\nimport { prepareRender, finishRender } from './hooks';\r\n\r\n/**\r\n * Renders a virtual DOM tree to an HTML string\r\n */\r\nexport function renderToString(vnode: VNode): string {\r\n // Reset hook state for this render\r\n prepareRender();\r\n \r\n // Render the tree to HTML\r\n const html = renderNodeToString(vnode);\r\n \r\n // Clean up after rendering\r\n finishRender();\r\n \r\n return html;\r\n}\r\n\r\n/**\r\n * Internal function to convert a virtual node to an HTML string\r\n */\r\nfunction renderNodeToString(vnode: VNode | string | number | boolean | null | undefined): string {\r\n // Handle primitive values\r\n if (vnode === null || vnode === undefined) return '';\r\n if (typeof vnode === 'boolean') return '';\r\n if (typeof vnode === 'number' || typeof vnode === 'string') return escapeHtml(String(vnode));\r\n \r\n // Handle function components\r\n if (typeof vnode.type === 'function') {\r\n const Component = vnode.type as Component;\r\n const renderedNode = Component(vnode.props || {});\r\n return renderNodeToString(renderedNode);\r\n }\r\n \r\n // Handle intrinsic elements (regular HTML tags)\r\n if (typeof vnode.type === 'string') {\r\n const tag = vnode.type;\r\n let attrs = '';\r\n let children = '';\r\n \r\n // Convert props to HTML attributes\r\n if (vnode.props) {\r\n for (const [key, value] of Object.entries(vnode.props)) {\r\n // Skip children prop as we handle it separately\r\n if (key === 'children') continue;\r\n \r\n // Skip event handlers (they start with 'on')\r\n if (key.startsWith('on')) continue;\r\n \r\n // Handle the className prop specially\r\n if (key === 'className') {\r\n attrs += ` class=\"${escapeHtml(value as string)}\"`;\r\n continue;\r\n }\r\n \r\n // Handle other attributes\r\n if (typeof value === 'string' || typeof value === 'number') {\r\n attrs += ` ${key}=\"${escapeHtml(String(value))}\"`;\r\n } else if (value === true) {\r\n // Boolean attributes\r\n attrs += ` ${key}`;\r\n }\r\n }\r\n }\r\n \r\n // Process children\r\n const childrenArray = (vnode.props?.children) \r\n ? Array.isArray(vnode.props.children) \r\n ? vnode.props.children \r\n : [vnode.props.children]\r\n : [];\r\n \r\n for (const child of childrenArray) {\r\n children += renderNodeToString(child);\r\n }\r\n \r\n // Self-closing tags\r\n const selfClosing = [\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 (selfClosing.includes(tag)) {\r\n return `<${tag}${attrs}/>`;\r\n }\r\n \r\n // Regular tags with closing\r\n return `<${tag}${attrs}>${children}</${tag}>`;\r\n }\r\n \r\n // Handle fragments\r\n if (vnode.type === Symbol.for('react.fragment')) {\r\n let fragmentOutput = '';\r\n const children = Array.isArray(vnode.props?.children)\r\n ? vnode.props.children\r\n : vnode.props?.children ? [vnode.props.children] : [];\r\n \r\n for (const child of children) {\r\n fragmentOutput += renderNodeToString(child);\r\n }\r\n \r\n return fragmentOutput;\r\n }\r\n \r\n // Fallback for unknown node types\r\n console.warn('Unknown vnode type:', vnode.type);\r\n return '';\r\n}\r\n\r\n/**\r\n * Escape HTML special characters to prevent XSS\r\n */\r\nfunction escapeHtml(text: string): string {\r\n return text\r\n .replace(/&/g, '&')\r\n .replace(/</g, '<')\r\n .replace(/>/g, '>')\r\n .replace(/\"/g, '"')\r\n .replace(/'/g, ''');\r\n}\r\n","import { createElement } from './jsx-runtime';\r\nimport { prepareRender, finishRender, setRenderCallback } from './hooks';\r\n\r\nexport { \r\n useState, \r\n useEffect, \r\n useMemo, \r\n useRef,\r\n useErrorBoundary \r\n} from './hooks';\r\n\r\nexport { createContext, useContext } from './context';\r\nexport { batchUpdates } from './batch';\r\nexport { jsx, jsxs, Fragment } from './jsx-runtime';\r\nexport { render, hydrate } from './renderer';\r\nexport { renderToString } from './server-renderer';\r\n\r\n// Re-export types for client-side usage\r\nexport type { Context } from './context';\r\nexport type { VNode } from './types';\r\n\r\n// Export server types for type checking\r\nexport type {\r\n Server,\r\n ServerConfig,\r\n User,\r\n DbConfig,\r\n MiddlewareFunction\r\n} from './server-types';\r\n\r\n// Provide placeholder server functionality\r\nexport const server = {\r\n async getServer() {\r\n throw new Error('Server module can only be used in Node.js environment');\r\n }\r\n};\r\n\r\nlet isHydrating = false;\r\n"],"names":["index","state","setState","ref","Component"],"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;AAE1B,MAAM,WAAW,OAAO,WAAW;AACnC,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;AAExE,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;ACpMO,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;ACxBA,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;AHrBzD;AGsBG,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;AC1GA,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;AC3BO,SAAS,eAAe,OAAsB;AAErC;AAGR,QAAA,OAAO,mBAAmB,KAAK;AAGxB;AAEN,SAAA;AACT;AAKA,SAAS,mBAAmB,OAAqE;ALtB1F;AKwBD,MAAA,UAAU,QAAQ,UAAU;AAAkB,WAAA;AAClD,MAAI,OAAO,UAAU;AAAkB,WAAA;AACvC,MAAI,OAAO,UAAU,YAAY,OAAO,UAAU;AAAiB,WAAA,WAAW,OAAO,KAAK,CAAC;AAGvF,MAAA,OAAO,MAAM,SAAS,YAAY;AACpC,UAAMC,aAAY,MAAM;AACxB,UAAM,eAAeA,WAAU,MAAM,SAAS,CAAE,CAAA;AAChD,WAAO,mBAAmB,YAAY;AAAA,EACxC;AAGI,MAAA,OAAO,MAAM,SAAS,UAAU;AAClC,UAAM,MAAM,MAAM;AAClB,QAAI,QAAQ;AACZ,QAAI,WAAW;AAGf,QAAI,MAAM,OAAO;AACJ,iBAAA,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,KAAK,GAAG;AAEtD,YAAI,QAAQ;AAAY;AAGpB,YAAA,IAAI,WAAW,IAAI;AAAG;AAG1B,YAAI,QAAQ,aAAa;AACd,mBAAA,WAAW,WAAW,KAAe,CAAC;AAC/C;AAAA,QACF;AAGA,YAAI,OAAO,UAAU,YAAY,OAAO,UAAU,UAAU;AAC1D,mBAAS,IAAI,GAAG,KAAK,WAAW,OAAO,KAAK,CAAC,CAAC;AAAA,QAAA,WACrC,UAAU,MAAM;AAEzB,mBAAS,IAAI,GAAG;AAAA,QAClB;AAAA,MACF;AAAA,IACF;AAGA,UAAM,kBAAiB,WAAM,UAAN,mBAAa,YAChC,MAAM,QAAQ,MAAM,MAAM,QAAQ,IAChC,MAAM,MAAM,WACZ,CAAC,MAAM,MAAM,QAAQ,IACvB;AAEJ,eAAW,SAAS,eAAe;AACjC,kBAAY,mBAAmB,KAAK;AAAA,IACtC;AAGA,UAAM,cAAc;AAAA,MAClB;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;AAG1C,QAAA,YAAY,SAAS,GAAG,GAAG;AACtB,aAAA,IAAI,GAAG,GAAG,KAAK;AAAA,IACxB;AAGA,WAAO,IAAI,GAAG,GAAG,KAAK,IAAI,QAAQ,KAAK,GAAG;AAAA,EAC5C;AAGA,MAAI,MAAM,SAAS,OAAO,IAAI,gBAAgB,GAAG;AAC/C,QAAI,iBAAiB;AACrB,UAAM,WAAW,MAAM,SAAQ,WAAM,UAAN,mBAAa,QAAQ,IAChD,MAAM,MAAM,aACZ,WAAM,UAAN,mBAAa,YAAW,CAAC,MAAM,MAAM,QAAQ,IAAI;AAErD,eAAW,SAAS,UAAU;AAC5B,wBAAkB,mBAAmB,KAAK;AAAA,IAC5C;AAEO,WAAA;AAAA,EACT;AAGQ,UAAA,KAAK,uBAAuB,MAAM,IAAI;AACvC,SAAA;AACT;AAKA,SAAS,WAAW,MAAsB;AACxC,SAAO,KACJ,QAAQ,MAAM,OAAO,EACrB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,QAAQ,EACtB,QAAQ,MAAM,QAAQ;AAC3B;ACzFO,MAAM,SAAS;AAAA,EACpB,MAAM,YAAY;AACV,UAAA,IAAI,MAAM,uDAAuD;AAAA,EACzE;AACF;"}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "frontend-hamroun",
|
3
|
-
"version": "1.2.
|
3
|
+
"version": "1.2.27",
|
4
4
|
"description": "A lightweight full-stack JavaScript framework",
|
5
5
|
"type": "module",
|
6
6
|
"main": "./dist/index.js",
|
@@ -25,6 +25,31 @@
|
|
25
25
|
"import": "./dist/server/index.js",
|
26
26
|
"require": "./dist/server/index.js"
|
27
27
|
},
|
28
|
+
"./server/database": {
|
29
|
+
"types": "./dist/server/database.d.ts",
|
30
|
+
"import": "./dist/server/database.js",
|
31
|
+
"require": "./dist/server/database.js"
|
32
|
+
},
|
33
|
+
"./server/auth": {
|
34
|
+
"types": "./dist/server/auth.d.ts",
|
35
|
+
"import": "./dist/server/auth.js",
|
36
|
+
"require": "./dist/server/auth.js"
|
37
|
+
},
|
38
|
+
"./server/middleware": {
|
39
|
+
"types": "./dist/server/middleware.d.ts",
|
40
|
+
"import": "./dist/server/middleware.js",
|
41
|
+
"require": "./dist/server/middleware.js"
|
42
|
+
},
|
43
|
+
"./server/api-router": {
|
44
|
+
"types": "./dist/server/api-router.d.ts",
|
45
|
+
"import": "./dist/server/api-router.js",
|
46
|
+
"require": "./dist/server/api-router.js"
|
47
|
+
},
|
48
|
+
"./server-types": {
|
49
|
+
"types": "./dist/server-types.d.ts",
|
50
|
+
"import": "./dist/server-types.js",
|
51
|
+
"require": "./dist/server-types.js"
|
52
|
+
},
|
28
53
|
"./ssr": {
|
29
54
|
"types": "./dist/server-renderer.d.ts",
|
30
55
|
"import": "./dist/server-renderer.js",
|
@@ -2,6 +2,14 @@ import express from 'express';
|
|
2
2
|
import path from 'path';
|
3
3
|
import { fileURLToPath } from 'url';
|
4
4
|
import fs from 'fs';
|
5
|
+
import dotenv from 'dotenv';
|
6
|
+
import { createServer } from 'http';
|
7
|
+
import { Server as SocketServer } from 'socket.io';
|
8
|
+
import compression from 'compression';
|
9
|
+
import cors from 'cors';
|
10
|
+
|
11
|
+
// Load environment variables
|
12
|
+
dotenv.config();
|
5
13
|
|
6
14
|
// Get __dirname equivalent in ESM
|
7
15
|
const __filename = fileURLToPath(import.meta.url);
|
@@ -10,9 +18,29 @@ const __dirname = path.dirname(__filename);
|
|
10
18
|
// Create Express app
|
11
19
|
const app = express();
|
12
20
|
const PORT = process.env.PORT || 3000;
|
21
|
+
const httpServer = createServer(app);
|
22
|
+
|
23
|
+
// Create socket.io server
|
24
|
+
const io = new SocketServer(httpServer, {
|
25
|
+
cors: {
|
26
|
+
origin: process.env.NODE_ENV === 'production' ? false : '*',
|
27
|
+
methods: ['GET', 'POST']
|
28
|
+
}
|
29
|
+
});
|
13
30
|
|
14
|
-
// Middleware
|
31
|
+
// Middleware setup
|
15
32
|
app.use(express.json());
|
33
|
+
app.use(express.urlencoded({ extended: true }));
|
34
|
+
app.use(compression()); // Add compression for better performance
|
35
|
+
app.use(cors()); // Enable CORS for API access
|
36
|
+
|
37
|
+
// Add request logging in development mode
|
38
|
+
if (process.env.NODE_ENV !== 'production') {
|
39
|
+
app.use((req, res, next) => {
|
40
|
+
console.log(`${new Date().toISOString()} - ${req.method} ${req.url}`);
|
41
|
+
next();
|
42
|
+
});
|
43
|
+
}
|
16
44
|
|
17
45
|
// For production, serve the built files
|
18
46
|
if (process.env.NODE_ENV === 'production') {
|
@@ -22,7 +50,22 @@ if (process.env.NODE_ENV === 'production') {
|
|
22
50
|
console.log('Running in development mode - Vite handles the frontend');
|
23
51
|
}
|
24
52
|
|
25
|
-
//
|
53
|
+
// Global in-memory store for demo purposes
|
54
|
+
// In a real app, you would use a database
|
55
|
+
const store = {
|
56
|
+
users: [
|
57
|
+
{ id: 1, name: 'User 1', email: 'user1@example.com' },
|
58
|
+
{ id: 2, name: 'User 2', email: 'user2@example.com' },
|
59
|
+
{ id: 3, name: 'User 3', email: 'user3@example.com' }
|
60
|
+
],
|
61
|
+
posts: [
|
62
|
+
{ id: 1, title: 'Post 1', content: 'Content for post 1', authorId: 1 },
|
63
|
+
{ id: 2, title: 'Post 2', content: 'Content for post 2', authorId: 2 },
|
64
|
+
{ id: 3, title: 'Post 3', content: 'Content for post 3', authorId: 1 }
|
65
|
+
]
|
66
|
+
};
|
67
|
+
|
68
|
+
// API endpoints - Core data
|
26
69
|
app.get('/api/hello', (req, res) => {
|
27
70
|
res.json({
|
28
71
|
message: "Hello from the API!",
|
@@ -31,11 +74,100 @@ app.get('/api/hello', (req, res) => {
|
|
31
74
|
"Server-side rendering",
|
32
75
|
"API routes",
|
33
76
|
"Component-based UI",
|
34
|
-
"React-like development experience"
|
77
|
+
"React-like development experience",
|
78
|
+
"WebSocket integration",
|
79
|
+
"Database connectivity",
|
80
|
+
"Authentication"
|
35
81
|
]
|
36
82
|
});
|
37
83
|
});
|
38
84
|
|
85
|
+
// Dynamic API route loading
|
86
|
+
const apiDir = path.join(__dirname, 'api');
|
87
|
+
if (fs.existsSync(apiDir)) {
|
88
|
+
console.log('Loading API routes from directory...');
|
89
|
+
|
90
|
+
// Function to map file paths to API routes
|
91
|
+
const registerApiRoutes = async (dir, routePrefix = '') => {
|
92
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
93
|
+
|
94
|
+
for (const entry of entries) {
|
95
|
+
const fullPath = path.join(dir, entry.name);
|
96
|
+
|
97
|
+
if (entry.isDirectory()) {
|
98
|
+
// Process directories recursively
|
99
|
+
await registerApiRoutes(fullPath, `${routePrefix}/${entry.name}`);
|
100
|
+
} else if (entry.name.endsWith('.js') || entry.name.endsWith('.ts')) {
|
101
|
+
// Process API file
|
102
|
+
try {
|
103
|
+
const fileName = entry.name.replace(/\.[^/.]+$/, ""); // Remove extension
|
104
|
+
const routePath = fileName === 'index'
|
105
|
+
? routePrefix
|
106
|
+
: fileName.startsWith('[') && fileName.endsWith(']')
|
107
|
+
? `${routePrefix}/:${fileName.slice(1, -1)}` // Convert [param] to :param
|
108
|
+
: `${routePrefix}/${fileName}`;
|
109
|
+
|
110
|
+
// Import the route module
|
111
|
+
const module = await import(`file://${fullPath}`);
|
112
|
+
|
113
|
+
// Register handlers for HTTP methods
|
114
|
+
['get', 'post', 'put', 'delete', 'patch'].forEach(method => {
|
115
|
+
if (typeof module[method] === 'function') {
|
116
|
+
console.log(` Registered ${method.toUpperCase()} ${routePath}`);
|
117
|
+
app[method](`/api${routePath}`, module[method]);
|
118
|
+
}
|
119
|
+
});
|
120
|
+
|
121
|
+
// Special case for 'delete' which might be named 'delete_' in some files
|
122
|
+
if (typeof module['delete_'] === 'function') {
|
123
|
+
console.log(` Registered DELETE ${routePath}`);
|
124
|
+
app.delete(`/api${routePath}`, module['delete_']);
|
125
|
+
}
|
126
|
+
} catch (err) {
|
127
|
+
console.error(`Error loading API route ${fullPath}:`, err);
|
128
|
+
}
|
129
|
+
}
|
130
|
+
}
|
131
|
+
};
|
132
|
+
|
133
|
+
// Start registering API routes
|
134
|
+
try {
|
135
|
+
await registerApiRoutes(apiDir);
|
136
|
+
console.log('API routes loaded successfully');
|
137
|
+
} catch (err) {
|
138
|
+
console.error('Error loading API routes:', err);
|
139
|
+
}
|
140
|
+
} else {
|
141
|
+
console.log('API directory not found, skipping API route loading');
|
142
|
+
}
|
143
|
+
|
144
|
+
// WebSocket setup
|
145
|
+
io.on('connection', (socket) => {
|
146
|
+
console.log('Client connected:', socket.id);
|
147
|
+
|
148
|
+
// Send welcome message
|
149
|
+
socket.emit('welcome', {
|
150
|
+
message: 'Connected to WebSocket server',
|
151
|
+
time: new Date().toISOString()
|
152
|
+
});
|
153
|
+
|
154
|
+
// Handle client messages
|
155
|
+
socket.on('message', (data) => {
|
156
|
+
console.log('Received message:', data);
|
157
|
+
// Broadcast to all clients
|
158
|
+
io.emit('broadcast', {
|
159
|
+
from: socket.id,
|
160
|
+
data,
|
161
|
+
time: new Date().toISOString()
|
162
|
+
});
|
163
|
+
});
|
164
|
+
|
165
|
+
// Handle disconnect
|
166
|
+
socket.on('disconnect', () => {
|
167
|
+
console.log('Client disconnected:', socket.id);
|
168
|
+
});
|
169
|
+
});
|
170
|
+
|
39
171
|
// In production or if using SSR, handle all requests
|
40
172
|
app.get('*', (req, res, next) => {
|
41
173
|
// Skip API routes
|
@@ -57,9 +189,19 @@ app.get('*', (req, res, next) => {
|
|
57
189
|
}
|
58
190
|
});
|
59
191
|
|
192
|
+
// Error handling middleware
|
193
|
+
app.use((err, req, res, next) => {
|
194
|
+
console.error('Server error:', err);
|
195
|
+
res.status(500).json({
|
196
|
+
error: process.env.NODE_ENV === 'production'
|
197
|
+
? 'Internal server error'
|
198
|
+
: err.message
|
199
|
+
});
|
200
|
+
});
|
201
|
+
|
60
202
|
// Start server
|
61
|
-
|
62
|
-
console.log(`
|
203
|
+
httpServer.listen(PORT, () => {
|
204
|
+
console.log(`Server running on http://localhost:${PORT}`);
|
63
205
|
|
64
206
|
if (process.env.NODE_ENV !== 'production') {
|
65
207
|
console.log(`Frontend development server will run on http://localhost:5173`);
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# Frontend Hamroun SSR Template
|
2
2
|
|
3
|
-
This is a
|
3
|
+
This is a comprehensive server-side rendering (SSR) example using Frontend Hamroun.
|
4
4
|
|
5
5
|
## Getting Started
|
6
6
|
|
@@ -16,26 +16,123 @@ This is a simple server-side rendering (SSR) example using Frontend Hamroun.
|
|
16
16
|
|
17
17
|
3. Open your browser at http://localhost:3000
|
18
18
|
|
19
|
-
##
|
19
|
+
## Core Features
|
20
20
|
|
21
|
-
This
|
21
|
+
This template demonstrates:
|
22
22
|
|
23
|
-
|
24
|
-
-
|
25
|
-
-
|
23
|
+
### Server-Side Rendering
|
24
|
+
- Pre-rendering of components on the server
|
25
|
+
- Hydration of server-rendered content on the client
|
26
|
+
- Data fetching during server rendering
|
27
|
+
- AI-powered meta tag generation
|
26
28
|
|
27
|
-
|
29
|
+
### API Integration
|
30
|
+
- RESTful API endpoints
|
31
|
+
- Dynamic API routing
|
32
|
+
- API middleware for validation and security
|
33
|
+
- File-based API structure
|
28
34
|
|
29
|
-
|
35
|
+
### Database Integration
|
36
|
+
- MongoDB, MySQL, and PostgreSQL support
|
37
|
+
- ORM-like query interface
|
38
|
+
- Connection pooling
|
39
|
+
- Transaction support
|
30
40
|
|
31
|
-
|
41
|
+
### Authentication & Authorization
|
42
|
+
- JWT-based authentication
|
43
|
+
- Role-based access control
|
44
|
+
- Password hashing and validation
|
45
|
+
- Token refresh mechanism
|
32
46
|
|
33
|
-
|
47
|
+
### Performance Optimization
|
48
|
+
- Caching strategies
|
49
|
+
- Response compression
|
50
|
+
- Static asset optimization
|
51
|
+
- Efficient metadata handling
|
34
52
|
|
35
|
-
|
53
|
+
## Implementation Examples
|
36
54
|
|
37
|
-
|
38
|
-
2. Use a routing solution for more complex page structures
|
39
|
-
3. Set up database connections and more robust API endpoints
|
55
|
+
### Creating Components
|
40
56
|
|
41
|
-
|
57
|
+
Use the `jsx` or `createElement` function from 'frontend-hamroun':
|
58
|
+
|
59
|
+
```jsx
|
60
|
+
import { jsx } from 'frontend-hamroun';
|
61
|
+
|
62
|
+
export default function MyComponent(props) {
|
63
|
+
return jsx('div', { className: "container" }, [
|
64
|
+
jsx('h1', {}, "Hello World"),
|
65
|
+
jsx('p', {}, `Props value: ${props.value}`)
|
66
|
+
]);
|
67
|
+
}
|
68
|
+
```
|
69
|
+
|
70
|
+
### Creating API Routes
|
71
|
+
|
72
|
+
Create files in the `api` directory following this pattern:
|
73
|
+
|
74
|
+
```typescript
|
75
|
+
// api/users/index.ts
|
76
|
+
import { Request, Response } from 'express';
|
77
|
+
|
78
|
+
export const get = (req: Request, res: Response) => {
|
79
|
+
res.json({ users: [...] });
|
80
|
+
};
|
81
|
+
|
82
|
+
export const post = (req: Request, res: Response) => {
|
83
|
+
// Create user
|
84
|
+
res.status(201).json({ success: true });
|
85
|
+
};
|
86
|
+
```
|
87
|
+
|
88
|
+
### Database Usage
|
89
|
+
|
90
|
+
```typescript
|
91
|
+
import { Server } from 'frontend-hamroun/server';
|
92
|
+
|
93
|
+
const server = new Server({
|
94
|
+
db: {
|
95
|
+
url: process.env.DATABASE_URL,
|
96
|
+
type: 'mongodb' // or 'mysql', 'postgres'
|
97
|
+
}
|
98
|
+
});
|
99
|
+
|
100
|
+
// Get typed database instance
|
101
|
+
const db = server.getDatabase();
|
102
|
+
const users = await db.query('SELECT * FROM users'); // For SQL
|
103
|
+
const docs = await db.getMongoDb().collection('users').find().toArray(); // For MongoDB
|
104
|
+
```
|
105
|
+
|
106
|
+
### Authentication
|
107
|
+
|
108
|
+
```typescript
|
109
|
+
import { AuthService } from 'frontend-hamroun/server';
|
110
|
+
|
111
|
+
const auth = new AuthService({
|
112
|
+
secret: process.env.JWT_SECRET,
|
113
|
+
expiresIn: '24h'
|
114
|
+
});
|
115
|
+
|
116
|
+
// Protect routes
|
117
|
+
app.get('/api/protected', auth.requireAuth(), (req, res) => {
|
118
|
+
res.json({ message: "Authenticated!" });
|
119
|
+
});
|
120
|
+
|
121
|
+
// Create user & login
|
122
|
+
const hashedPassword = await auth.hashPassword(password);
|
123
|
+
const token = auth.generateToken(user);
|
124
|
+
```
|
125
|
+
|
126
|
+
### Advanced Middleware
|
127
|
+
|
128
|
+
```typescript
|
129
|
+
import { rateLimit, requestLogger, errorHandler } from 'frontend-hamroun/server';
|
130
|
+
|
131
|
+
app.use(requestLogger);
|
132
|
+
app.use('/api', rateLimit({ windowMs: 15 * 60 * 1000, max: 100 }));
|
133
|
+
app.use(errorHandler);
|
134
|
+
```
|
135
|
+
|
136
|
+
## Next Steps
|
137
|
+
|
138
|
+
Explore the full API documentation for more advanced features and customization options.
|
@@ -2,6 +2,14 @@ import express from 'express';
|
|
2
2
|
import { fileURLToPath } from 'url';
|
3
3
|
import { dirname, join } from 'path';
|
4
4
|
import { renderToString } from 'frontend-hamroun';
|
5
|
+
import { Database } from 'frontend-hamroun';
|
6
|
+
import { AuthService } from 'frontend-hamroun';
|
7
|
+
import { requestLogger, errorHandler, notFoundHandler, rateLimit } from 'frontend-hamroun';
|
8
|
+
import dotenv from 'dotenv';
|
9
|
+
import fetch from 'node-fetch';
|
10
|
+
|
11
|
+
// Load environment variables
|
12
|
+
dotenv.config();
|
5
13
|
|
6
14
|
// Get directory name in ESM
|
7
15
|
const __filename = fileURLToPath(import.meta.url);
|
@@ -11,6 +19,67 @@ const __dirname = dirname(__filename);
|
|
11
19
|
const app = express();
|
12
20
|
const port = process.env.PORT ? parseInt(process.env.PORT) : 3000;
|
13
21
|
|
22
|
+
// Add middleware
|
23
|
+
app.use(express.json());
|
24
|
+
app.use(express.urlencoded({ extended: true }));
|
25
|
+
app.use(requestLogger);
|
26
|
+
|
27
|
+
// Rate limiting for API routes
|
28
|
+
app.use('/api', rateLimit({
|
29
|
+
windowMs: 15 * 60 * 1000, // 15 minutes
|
30
|
+
max: 100 // limit each IP to 100 requests per windowMs
|
31
|
+
}));
|
32
|
+
|
33
|
+
// Configure database if connection string is provided
|
34
|
+
let db = null;
|
35
|
+
if (process.env.DATABASE_URL) {
|
36
|
+
db = new Database({
|
37
|
+
url: process.env.DATABASE_URL,
|
38
|
+
type: (process.env.DATABASE_TYPE || 'mongodb') as 'mongodb' | 'mysql' | 'postgres'
|
39
|
+
});
|
40
|
+
|
41
|
+
// Connect to database
|
42
|
+
try {
|
43
|
+
await db.connect();
|
44
|
+
console.log('Database connected successfully');
|
45
|
+
} catch (error) {
|
46
|
+
console.error('Database connection failed:', error);
|
47
|
+
}
|
48
|
+
}
|
49
|
+
|
50
|
+
// Configure auth if secret is provided
|
51
|
+
let auth = null;
|
52
|
+
if (process.env.JWT_SECRET) {
|
53
|
+
auth = new AuthService({
|
54
|
+
secret: process.env.JWT_SECRET,
|
55
|
+
expiresIn: process.env.JWT_EXPIRES_IN || '24h'
|
56
|
+
});
|
57
|
+
|
58
|
+
// Add auth middleware
|
59
|
+
app.use(auth.initialize());
|
60
|
+
|
61
|
+
// Example protected route
|
62
|
+
app.get('/api/protected', auth.requireAuth(), (req, res) => {
|
63
|
+
res.json({ message: 'Protected route accessed successfully' });
|
64
|
+
});
|
65
|
+
|
66
|
+
// Example role-based protection
|
67
|
+
app.get('/api/admin', auth.requireRoles(['admin']), (req, res) => {
|
68
|
+
res.json({ message: 'Admin route accessed successfully' });
|
69
|
+
});
|
70
|
+
|
71
|
+
// Login route
|
72
|
+
app.post('/api/login', async (req, res) => {
|
73
|
+
const { username, password } = req.body;
|
74
|
+
|
75
|
+
// In a real app, fetch user from database
|
76
|
+
const user = { id: 1, username, roles: ['user'] };
|
77
|
+
const token = auth.generateToken(user);
|
78
|
+
|
79
|
+
res.json({ token, user: { id: user.id, username: user.username, roles: user.roles } });
|
80
|
+
});
|
81
|
+
}
|
82
|
+
|
14
83
|
// Serve static files from public directory
|
15
84
|
app.use(express.static(join(__dirname, 'public')));
|
16
85
|
|
@@ -23,6 +92,42 @@ app.get('/api/page-data', (req, res) => {
|
|
23
92
|
});
|
24
93
|
});
|
25
94
|
|
95
|
+
// Meta tag generation function (using local logic for simplicity)
|
96
|
+
async function generateMetaTags(pageContent) {
|
97
|
+
// Extract title from page content
|
98
|
+
const title = pageContent.split('\n')[0].replace(/[#*]/g, '').trim() ||
|
99
|
+
'Frontend Hamroun SSR Page';
|
100
|
+
|
101
|
+
// Generate description from content
|
102
|
+
const description = pageContent.substring(0, 150) + '...';
|
103
|
+
|
104
|
+
// Extract keywords
|
105
|
+
const keywords = pageContent
|
106
|
+
.toLowerCase()
|
107
|
+
.replace(/[^\w\s]/g, '')
|
108
|
+
.split(/\s+/)
|
109
|
+
.filter(w => w.length > 3)
|
110
|
+
.slice(0, 5)
|
111
|
+
.join(', ');
|
112
|
+
|
113
|
+
return {
|
114
|
+
title,
|
115
|
+
description,
|
116
|
+
keywords
|
117
|
+
};
|
118
|
+
}
|
119
|
+
|
120
|
+
// Helper function to check if file exists
|
121
|
+
async function fileExists(path) {
|
122
|
+
try {
|
123
|
+
const fs = await import('fs/promises');
|
124
|
+
await fs.access(path);
|
125
|
+
return true;
|
126
|
+
} catch {
|
127
|
+
return false;
|
128
|
+
}
|
129
|
+
}
|
130
|
+
|
26
131
|
// Implement basic SSR without relying on complex server functionality
|
27
132
|
app.get('*', async (req, res) => {
|
28
133
|
try {
|
@@ -60,6 +165,17 @@ app.get('*', async (req, res) => {
|
|
60
165
|
// Import the component
|
61
166
|
const { default: PageComponent } = await import(componentPath);
|
62
167
|
|
168
|
+
// Generate page content for meta tags
|
169
|
+
const pageContent = `
|
170
|
+
Frontend Hamroun SSR Page
|
171
|
+
This is a server-rendered page using the Frontend Hamroun framework.
|
172
|
+
Path: ${req.path}
|
173
|
+
Timestamp: ${new Date().toISOString()}
|
174
|
+
`;
|
175
|
+
|
176
|
+
// Generate meta tags
|
177
|
+
const metaTags = await generateMetaTags(pageContent);
|
178
|
+
|
63
179
|
// Render the component to string
|
64
180
|
const content = renderToString(PageComponent());
|
65
181
|
|
@@ -70,15 +186,36 @@ app.get('*', async (req, res) => {
|
|
70
186
|
<head>
|
71
187
|
<meta charset="UTF-8">
|
72
188
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
73
|
-
|
189
|
+
|
190
|
+
<!-- Generated Meta Tags -->
|
191
|
+
<title>${metaTags.title}</title>
|
192
|
+
<meta name="description" content="${metaTags.description}">
|
193
|
+
<meta name="keywords" content="${metaTags.keywords}">
|
194
|
+
|
195
|
+
<!-- Open Graph Meta Tags -->
|
196
|
+
<meta property="og:title" content="${metaTags.title}">
|
197
|
+
<meta property="og:description" content="${metaTags.description}">
|
198
|
+
<meta property="og:type" content="website">
|
199
|
+
<meta property="og:url" content="${req.protocol}://${req.get('host')}${req.originalUrl}">
|
200
|
+
|
74
201
|
<!-- Import Tailwind-like styles for quick styling -->
|
75
202
|
<link href="https://cdn.jsdelivr.net/npm/daisyui@3.7.4/dist/full.css" rel="stylesheet" type="text/css" />
|
76
203
|
<script src="https://cdn.tailwindcss.com"></script>
|
204
|
+
|
77
205
|
<!-- Client-side script for hydration -->
|
78
206
|
<script type="module" src="/assets/client.js"></script>
|
79
207
|
</head>
|
80
208
|
<body>
|
81
209
|
<div id="app">${content}</div>
|
210
|
+
|
211
|
+
<!-- Add initial state for hydration -->
|
212
|
+
<script>
|
213
|
+
window.__INITIAL_STATE__ = ${JSON.stringify({
|
214
|
+
path: req.path,
|
215
|
+
timestamp: new Date().toISOString(),
|
216
|
+
metaTags
|
217
|
+
})};
|
218
|
+
</script>
|
82
219
|
</body>
|
83
220
|
</html>
|
84
221
|
`);
|
@@ -100,24 +237,39 @@ app.get('*', async (req, res) => {
|
|
100
237
|
}
|
101
238
|
});
|
102
239
|
|
103
|
-
//
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
240
|
+
// Add error handler middleware
|
241
|
+
app.use(errorHandler);
|
242
|
+
|
243
|
+
// Add not found handler for API routes that weren't caught
|
244
|
+
app.use(notFoundHandler);
|
245
|
+
|
246
|
+
// Graceful shutdown function to close database connections
|
247
|
+
function gracefulShutdown() {
|
248
|
+
console.log('Shutting down server...');
|
249
|
+
|
250
|
+
// Close database connection if it exists
|
251
|
+
if (db) {
|
252
|
+
db.disconnect()
|
253
|
+
.then(() => console.log('Database disconnected'))
|
254
|
+
.catch(err => console.error('Error disconnecting from database:', err))
|
255
|
+
.finally(() => process.exit(0));
|
256
|
+
} else {
|
257
|
+
process.exit(0);
|
111
258
|
}
|
112
259
|
}
|
113
260
|
|
114
261
|
// Start the server
|
115
|
-
app.listen(port, () => {
|
262
|
+
const server = app.listen(port, () => {
|
116
263
|
console.log(`Server running at http://localhost:${port}`);
|
264
|
+
console.log(`Available API endpoints:`);
|
265
|
+
console.log(` - GET /api/page-data`);
|
266
|
+
console.log(` - POST /api/login`);
|
267
|
+
if (process.env.JWT_SECRET) {
|
268
|
+
console.log(` - GET /api/protected (requires authentication)`);
|
269
|
+
console.log(` - GET /api/admin (requires admin role)`);
|
270
|
+
}
|
117
271
|
});
|
118
272
|
|
119
273
|
// Handle graceful shutdown
|
120
|
-
process.on('SIGINT',
|
121
|
-
|
122
|
-
process.exit(0);
|
123
|
-
});
|
274
|
+
process.on('SIGINT', gracefulShutdown);
|
275
|
+
process.on('SIGTERM', gracefulShutdown);
|