frontend-hamroun 1.2.83 → 1.2.85
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/bin/cli.js +57 -869
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.client.cjs +1 -1
- package/dist/index.client.cjs.map +1 -1
- package/dist/index.client.js +2 -2
- package/dist/index.client.js.map +1 -1
- package/dist/index.js +116 -136
- package/dist/index.js.map +1 -1
- package/dist/jsx-runtime.cjs.map +1 -1
- package/dist/jsx-runtime.js.map +1 -1
- package/dist/renderer-DaVfBeVi.cjs +2 -0
- package/dist/renderer-DaVfBeVi.cjs.map +1 -0
- package/dist/renderer-nfT7XSpo.js +61 -0
- package/dist/renderer-nfT7XSpo.js.map +1 -0
- package/dist/server-renderer-B5b0Q0ck.cjs +2 -0
- package/dist/server-renderer-B5b0Q0ck.cjs.map +1 -0
- package/dist/{server-renderer-C1WXH-zV.js → server-renderer-C4MB-jAp.js} +6 -39
- package/dist/server-renderer-C4MB-jAp.js.map +1 -0
- package/dist/server-renderer.cjs +1 -1
- package/dist/server-renderer.js +1 -1
- package/package.json +1 -1
- package/templates/basic-app/build.d.ts +2 -0
- package/templates/basic-app/build.d.ts.map +1 -0
- package/templates/basic-app/dev.d.ts +2 -0
- package/templates/basic-app/dev.d.ts.map +1 -0
- package/templates/basic-app/esbuild.config.d.ts +2 -0
- package/templates/basic-app/esbuild.config.d.ts.map +1 -0
- package/templates/basic-app/postcss.config.d.ts +8 -0
- package/templates/basic-app/postcss.config.d.ts.map +1 -0
- package/templates/basic-app/server.d.ts +2 -0
- package/templates/basic-app/server.d.ts.map +1 -0
- package/templates/basic-app/src/App.d.ts +2 -0
- package/templates/basic-app/src/App.d.ts.map +1 -0
- package/templates/basic-app/src/App.js +148 -0
- package/templates/basic-app/src/App.tsx +397 -19
- package/templates/basic-app/src/client.d.ts +2 -0
- package/templates/basic-app/src/client.d.ts.map +1 -0
- package/templates/basic-app/src/client.js +6 -0
- package/templates/basic-app/src/components/Counter.d.ts +4 -0
- package/templates/basic-app/src/components/Counter.d.ts.map +1 -0
- package/templates/basic-app/src/components/Counter.js +9 -0
- package/templates/basic-app/src/jsx-shim.d.ts +8 -0
- package/templates/basic-app/src/jsx-shim.d.ts.map +1 -0
- package/templates/basic-app/src/main.d.ts +2 -0
- package/templates/basic-app/src/main.d.ts.map +1 -0
- package/templates/basic-app/src/main.js +57 -0
- package/templates/basic-app/src/server.d.ts +2 -0
- package/templates/basic-app/src/server.d.ts.map +1 -0
- package/templates/basic-app/tailwind.config.d.ts +9 -0
- package/templates/basic-app/tailwind.config.d.ts.map +1 -0
- package/templates/basic-app/vite.config.d.ts +3 -0
- package/templates/basic-app/vite.config.d.ts.map +1 -0
- package/templates/basic-app/vite.config.js +7 -0
- package/templates/complete-app/api/hello.d.ts +1 -0
- package/templates/complete-app/api/hello.d.ts.map +1 -0
- package/templates/complete-app/client.d.ts +2 -0
- package/templates/complete-app/client.d.ts.map +1 -0
- package/templates/complete-app/lib/frontend-hamroun.d.ts +18 -0
- package/templates/complete-app/lib/frontend-hamroun.d.ts.map +1 -0
- package/templates/complete-app/pages/about.d.ts +7 -0
- package/templates/complete-app/pages/about.d.ts.map +1 -0
- package/templates/complete-app/pages/index.d.ts +7 -0
- package/templates/complete-app/pages/index.d.ts.map +1 -0
- package/templates/complete-app/pages/wasm-demo.d.ts +7 -0
- package/templates/complete-app/pages/wasm-demo.d.ts.map +1 -0
- package/templates/complete-app/public/client.d.ts +17 -0
- package/templates/complete-app/public/client.d.ts.map +1 -0
- package/templates/complete-app/server.d.ts +2 -0
- package/templates/complete-app/server.d.ts.map +1 -0
- package/templates/complete-app/server.js +236 -218
- package/templates/complete-app/src/App.d.ts +2 -0
- package/templates/complete-app/src/App.d.ts.map +1 -0
- package/templates/complete-app/src/App.js +27 -0
- package/templates/complete-app/src/client.d.ts +2 -0
- package/templates/complete-app/src/client.d.ts.map +1 -0
- package/templates/complete-app/src/client.js +52 -0
- package/templates/complete-app/src/pages/index.d.ts +2 -0
- package/templates/complete-app/src/pages/index.d.ts.map +1 -0
- package/templates/complete-app/src/pages/index.js +19 -0
- package/templates/complete-app/src/server.d.ts +2 -0
- package/templates/complete-app/src/server.d.ts.map +1 -0
- package/templates/complete-app/src/server.js +192 -0
- package/templates/complete-app/vite.config.d.ts +3 -0
- package/templates/complete-app/vite.config.d.ts.map +1 -0
- package/templates/complete-app/vite.config.js +29 -57
- package/templates/fullstack-app/api/hello.d.ts +4 -0
- package/templates/fullstack-app/api/hello.d.ts.map +1 -0
- package/templates/fullstack-app/api/hello.js +14 -11
- package/templates/fullstack-app/api/users/[id].d.ts +5 -0
- package/templates/fullstack-app/api/users/[id].d.ts.map +1 -0
- package/templates/fullstack-app/api/users/[id].js +52 -0
- package/templates/fullstack-app/api/users/index.d.ts +4 -0
- package/templates/fullstack-app/api/users/index.d.ts.map +1 -0
- package/templates/fullstack-app/api/users/index.js +25 -0
- package/templates/fullstack-app/build/main.d.ts +211 -0
- package/templates/fullstack-app/build/main.d.ts.map +1 -0
- package/templates/fullstack-app/build.d.ts +2 -0
- package/templates/fullstack-app/build.d.ts.map +1 -0
- package/templates/fullstack-app/build.js +190 -0
- package/templates/fullstack-app/postcss.config.d.ts +5 -0
- package/templates/fullstack-app/postcss.config.d.ts.map +1 -0
- package/templates/fullstack-app/process-tailwind.d.ts +2 -0
- package/templates/fullstack-app/process-tailwind.d.ts.map +1 -0
- package/templates/fullstack-app/public/route-handler.d.ts +1 -0
- package/templates/fullstack-app/public/route-handler.d.ts.map +1 -0
- package/templates/fullstack-app/server.d.ts +2 -0
- package/templates/fullstack-app/server.d.ts.map +1 -0
- package/templates/fullstack-app/server.js +428 -266
- package/templates/fullstack-app/src/client.d.ts +2 -0
- package/templates/fullstack-app/src/client.d.ts.map +1 -0
- package/templates/fullstack-app/src/components/ClientHome.d.ts +1 -0
- package/templates/fullstack-app/src/components/ClientHome.d.ts.map +1 -0
- package/templates/fullstack-app/src/components/ClientHome.js +1 -0
- package/templates/fullstack-app/src/components/ErrorBoundary.d.ts +7 -0
- package/templates/fullstack-app/src/components/ErrorBoundary.d.ts.map +1 -0
- package/templates/fullstack-app/src/components/ErrorBoundary.js +12 -0
- package/templates/fullstack-app/src/components/Layout.d.ts +7 -0
- package/templates/fullstack-app/src/components/Layout.d.ts.map +1 -0
- package/templates/fullstack-app/src/components/Layout.js +4 -0
- package/templates/fullstack-app/src/components/StateDemo.d.ts +2 -0
- package/templates/fullstack-app/src/components/StateDemo.d.ts.map +1 -0
- package/templates/fullstack-app/src/components/StateDemo.js +86 -0
- package/templates/fullstack-app/src/components/UserList.d.ts +11 -0
- package/templates/fullstack-app/src/components/UserList.d.ts.map +1 -0
- package/templates/fullstack-app/src/components/UserList.js +7 -0
- package/templates/fullstack-app/src/config.d.ts +29 -0
- package/templates/fullstack-app/src/config.d.ts.map +1 -0
- package/templates/fullstack-app/src/config.js +36 -0
- package/templates/fullstack-app/src/data/api.d.ts +35 -0
- package/templates/fullstack-app/src/data/api.d.ts.map +1 -0
- package/templates/fullstack-app/src/data/api.js +173 -0
- package/templates/fullstack-app/src/main.d.ts +7 -0
- package/templates/fullstack-app/src/main.d.ts.map +1 -0
- package/templates/fullstack-app/src/main.js +130 -0
- package/templates/fullstack-app/src/middleware.d.ts +10 -0
- package/templates/fullstack-app/src/middleware.d.ts.map +1 -0
- package/templates/fullstack-app/src/middleware.js +14 -0
- package/templates/fullstack-app/src/pages/404.d.ts +4 -0
- package/templates/fullstack-app/src/pages/404.d.ts.map +1 -0
- package/templates/fullstack-app/src/pages/404.js +4 -0
- package/templates/fullstack-app/src/pages/[id].d.ts +1 -0
- package/templates/fullstack-app/src/pages/[id].d.ts.map +1 -0
- package/templates/fullstack-app/src/pages/[id].js +1 -0
- package/templates/fullstack-app/src/pages/_app.d.ts +6 -0
- package/templates/fullstack-app/src/pages/_app.d.ts.map +1 -0
- package/templates/fullstack-app/src/pages/_app.js +6 -0
- package/templates/fullstack-app/src/pages/_document.d.ts +7 -0
- package/templates/fullstack-app/src/pages/_document.d.ts.map +1 -0
- package/templates/fullstack-app/src/pages/_document.js +4 -0
- package/templates/fullstack-app/src/pages/_error.d.ts +4 -0
- package/templates/fullstack-app/src/pages/_error.d.ts.map +1 -0
- package/templates/fullstack-app/src/pages/_error.js +8 -0
- package/templates/fullstack-app/src/pages/about/index.d.ts +5 -0
- package/templates/fullstack-app/src/pages/about/index.d.ts.map +1 -0
- package/templates/fullstack-app/src/pages/about/index.js +6 -0
- package/templates/fullstack-app/src/pages/about.d.ts +10 -0
- package/templates/fullstack-app/src/pages/about.d.ts.map +1 -0
- package/templates/fullstack-app/src/pages/about.js +21 -0
- package/templates/fullstack-app/src/pages/api/users/[id].d.ts +6 -0
- package/templates/fullstack-app/src/pages/api/users/[id].d.ts.map +1 -0
- package/templates/fullstack-app/src/pages/api/users/[id].js +51 -0
- package/templates/fullstack-app/src/pages/api/users/index.d.ts +4 -0
- package/templates/fullstack-app/src/pages/api/users/index.d.ts.map +1 -0
- package/templates/fullstack-app/src/pages/api/users/index.js +33 -0
- package/templates/fullstack-app/src/pages/index.d.ts +21 -0
- package/templates/fullstack-app/src/pages/index.d.ts.map +1 -0
- package/templates/fullstack-app/src/pages/index.js +66 -0
- package/templates/fullstack-app/src/pages/users/[id].d.ts +38 -0
- package/templates/fullstack-app/src/pages/users/[id].d.ts.map +1 -0
- package/templates/fullstack-app/src/pages/users/[id].js +79 -0
- package/templates/fullstack-app/src/pages/users.d.ts +14 -0
- package/templates/fullstack-app/src/pages/users.d.ts.map +1 -0
- package/templates/fullstack-app/src/pages/users.js +14 -0
- package/templates/fullstack-app/src/pages/wasm-demo.d.ts +1 -0
- package/templates/fullstack-app/src/pages/wasm-demo.d.ts.map +1 -0
- package/templates/fullstack-app/src/pages/wasm-demo.js +2 -0
- package/templates/fullstack-app/src/router.d.ts +22 -0
- package/templates/fullstack-app/src/router.d.ts.map +1 -0
- package/templates/fullstack-app/src/router.js +210 -0
- package/templates/fullstack-app/tailwind.config.d.ts +3 -0
- package/templates/fullstack-app/tailwind.config.d.ts.map +1 -0
- package/templates/fullstack-app/vite.config.d.ts +3 -0
- package/templates/fullstack-app/vite.config.d.ts.map +1 -0
- package/templates/ssr-template/dist/client/assets/main-D-VH3xOb.d.ts +2 -0
- package/templates/ssr-template/dist/client/assets/main-D-VH3xOb.d.ts.map +1 -0
- package/templates/ssr-template/dist/client.d.ts +85 -0
- package/templates/ssr-template/dist/client.d.ts.map +1 -0
- package/templates/ssr-template/dist/server.d.ts +2 -0
- package/templates/ssr-template/dist/server.d.ts.map +1 -0
- package/templates/ssr-template/esbuild.config.d.ts +2 -0
- package/templates/ssr-template/esbuild.config.d.ts.map +1 -0
- package/templates/ssr-template/jsx-shim.d.ts +2 -0
- package/templates/ssr-template/jsx-shim.d.ts.map +1 -0
- package/templates/ssr-template/src/App.d.ts +2 -0
- package/templates/ssr-template/src/App.d.ts.map +1 -0
- package/templates/ssr-template/src/App.js +625 -0
- package/templates/ssr-template/src/App.tsx +43 -18
- package/templates/ssr-template/src/client.d.ts +2 -0
- package/templates/ssr-template/src/client.d.ts.map +1 -0
- package/templates/ssr-template/src/client.js +3 -0
- package/templates/ssr-template/src/server.d.ts +2 -0
- package/templates/ssr-template/src/server.d.ts.map +1 -0
- package/templates/ssr-template/src/server.js +29 -0
- package/templates/ssr-template/vite.config.d.ts +3 -0
- package/templates/ssr-template/vite.config.d.ts.map +1 -0
- package/templates/ssr-template/vite.config.js +30 -0
- package/templates/wasm/build-wasm.d.ts +2 -0
- package/templates/wasm/build-wasm.d.ts.map +1 -0
- package/templates/wasm/dist/assets/index-BNqTDBdE.d.ts +30 -0
- package/templates/wasm/dist/assets/index-BNqTDBdE.d.ts.map +1 -0
- package/templates/wasm/dist/wasm_exec.d.ts +1 -0
- package/templates/wasm/dist/wasm_exec.d.ts.map +1 -0
- package/templates/wasm/esbuild.config.d.ts +2 -0
- package/templates/wasm/esbuild.config.d.ts.map +1 -0
- package/templates/wasm/go/wasm_exec.d.ts +1 -0
- package/templates/wasm/go/wasm_exec.d.ts.map +1 -0
- package/templates/wasm/jsx-shim.d.ts +5 -0
- package/templates/wasm/jsx-shim.d.ts.map +1 -0
- package/templates/wasm/public/wasm_exec.d.ts +1 -0
- package/templates/wasm/public/wasm_exec.d.ts.map +1 -0
- package/templates/wasm/src/App.d.ts +2 -0
- package/templates/wasm/src/App.d.ts.map +1 -0
- package/templates/wasm/src/App.js +381 -0
- package/templates/wasm/src/client.d.ts +2 -0
- package/templates/wasm/src/client.d.ts.map +1 -0
- package/templates/wasm/src/client.js +210 -0
- package/templates/wasm/src/index.d.ts +2 -0
- package/templates/wasm/src/index.d.ts.map +1 -0
- package/templates/wasm/src/index.js +20 -0
- package/templates/wasm/src/server.d.ts +2 -0
- package/templates/wasm/src/server.d.ts.map +1 -0
- package/templates/wasm/src/server.js +131 -0
- package/templates/wasm/vite.config.d.ts +3 -0
- package/templates/wasm/vite.config.d.ts.map +1 -0
- package/templates/wasm/vite.config.js +36 -0
- package/templates/wasm/wasm-loader.d.ts +6 -0
- package/templates/wasm/wasm-loader.d.ts.map +1 -0
- package/dist/renderer-BL3gq8cW.cjs +0 -2
- package/dist/renderer-BL3gq8cW.cjs.map +0 -1
- package/dist/renderer-Dyy-o05F.js +0 -52
- package/dist/renderer-Dyy-o05F.js.map +0 -1
- package/dist/server-renderer-C1WXH-zV.js.map +0 -1
- package/dist/server-renderer-Chs-nmJm.cjs +0 -2
- package/dist/server-renderer-Chs-nmJm.cjs.map +0 -1
package/dist/jsx-runtime.js.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"jsx-runtime.js","sources":["../src/jsx-runtime.ts"],"sourcesContent":["import type {
|
1
|
+
{"version":3,"file":"jsx-runtime.js","sources":["../src/jsx-runtime.ts"],"sourcesContent":["import type { VNode, ComponentType, Key, ReactElement } from './types.js';\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\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"],"names":[],"mappings":"AAEA,SAAS,IAAI,MAAyB,OAAmB;AACvD,UAAQ,IAAI,kBAAkB,EAAE,MAAM,OAAO;AACvC,QAAA,iBAAiB,EAAE,GAAG,MAAM;AAG9B,MAAA,UAAU,SAAS,GAAG;AACxB,mBAAe,WAAW,MAAM,UAAU,MAAM,KAAK,WAAW,CAAC;AAAA,EAAA;AAG5D,SAAA,EAAE,MAAM,OAAO,eAAe;AACvC;AAEA,MAAM,WAAW,CAAC,EAAE,eAAkC;AAEtD,eAAe,cAAc,OAAmC;AACtD,UAAA,IAAI,0BAA0B,KAAK;AAG3C,MAAI,SAAS,MAAM;AACV,WAAA,SAAS,eAAe,EAAE;AAAA,EAAA;AAG/B,MAAA,OAAO,UAAU,WAAW;AACvB,WAAA,SAAS,eAAe,EAAE;AAAA,EAAA;AAGnC,MAAI,OAAO,UAAU,YAAY,OAAO,UAAU,UAAU;AAC1D,WAAO,SAAS,eAAe,OAAO,KAAK,CAAC;AAAA,EAAA;AAI1C,MAAA,MAAM,QAAQ,KAAK,GAAG;AAClB,UAAA,WAAW,SAAS,uBAAuB;AACjD,eAAW,SAAS,OAAO;AACnB,YAAA,OAAO,MAAM,cAAc,KAAK;AACtC,eAAS,YAAY,IAAI;AAAA,IAAA;AAEpB,WAAA;AAAA,EAAA;AAIT,MAAI,UAAU,SAAS,MAAM,UAAU,QAAW;AAC1C,UAAA,EAAE,MAAM,MAAA,IAAU;AAGpB,QAAA,OAAO,SAAS,YAAY;AAC1B,UAAA;AACF,cAAM,SAAS,MAAM,KAAK,SAAS,CAAA,CAAE;AAC/B,cAAA,OAAO,MAAM,cAAc,MAAM;AACvC,YAAI,gBAAgB,SAAS;AAC3B,eAAK,aAAa,qBAAqB,KAAK,QAAQ,KAAK,UAAU;AAAA,QAAA;AAE9D,eAAA;AAAA,eACA,OAAO;AACN,gBAAA,MAAM,8BAA8B,KAAK;AAC1C,eAAA,SAAS,eAAe,EAAE;AAAA,MAAA;AAAA,IACnC;AAII,UAAA,UAAU,SAAS,cAAc,IAAc;AAG1C,eAAA,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,SAAS,CAAA,CAAE,GAAG;AACtD,UAAI,QAAQ,WAAY;AACxB,UAAI,IAAI,WAAW,IAAI,KAAK,OAAO,UAAU,YAAY;AACvD,cAAM,YAAY,IAAI,YAAY,EAAE,MAAM,CAAC;AAErC,cAAA,kBAAmB,QAAgB,WAAW,SAAS;AAC7D,YAAI,iBAAiB;AACX,kBAAA,oBAAoB,WAAW,eAAe;AAAA,QAAA;AAIhD,gBAAA,iBAAiB,WAAW,KAAsB;AACtD,YAAA,CAAE,QAAgB,UAAU;AAC7B,kBAAgB,WAAW,CAAC;AAAA,QAAA;AAE9B,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,MAAA;AAAA,IACzC;AAIF,UAAM,WAAW,OAAO;AACxB,QAAI,YAAY,MAAM;AACd,YAAA,aAAa,MAAM,QAAQ,QAAQ,IAAI,SAAS,SAAS,CAAC,QAAQ;AACxE,iBAAW,SAAS,YAAY;AACxB,cAAA,YAAY,MAAM,cAAc,KAAK;AAC3C,gBAAQ,YAAY,SAAS;AAAA,MAAA;AAAA,IAC/B;AAGK,WAAA;AAAA,EAAA;AAIT,SAAO,SAAS,eAAe,OAAO,KAAK,CAAC;AAC9C;AAYA,MAAM,aAAa;AAAA,EACjB;AAAA,EACA,MAAM;AAAA,EACN,QAAQ;AAAA,EACR;AAAA,EACA;AACF;"}
|
@@ -0,0 +1,2 @@
|
|
1
|
+
"use strict";const e=require("./jsx-runtime.cjs"),r=require("./server-renderer-B5b0Q0ck.cjs"),n=new Map;async function t(n,o){try{r.setRenderCallback(t,n,o);r.prepareRender(n);const s=await e.createElement(n);if(o instanceof Element)o.innerHTML="";else for(;o.firstChild;)o.removeChild(o.firstChild);o.appendChild(s),r.finishRender(),console.log("Render completed successfully")}catch(s){throw console.error("Error during render:",s),r.finishRender(),s}}exports.createContext=function(e){const r=Symbol("context");return{Provider:({value:e,children:t})=>(n.set(r,e),t),Consumer:({children:t})=>t(n.get(r)??e),displayName:"Context"}},exports.hydrate=async function(e,r){try{console.log("Starting hydration..."),await t(e,r),console.log("Hydration completed successfully")}catch(n){throw console.error("Error during hydration:",n),n}},exports.render=t,exports.useContext=function(e){return{}};
|
2
|
+
//# sourceMappingURL=renderer-DaVfBeVi.cjs.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"renderer-DaVfBeVi.cjs","sources":["../src/context.ts","../src/renderer.ts"],"sourcesContent":["import type { Context, ComponentType } from './types.js';\r\n\r\nconst contexts = new Map<symbol, any>();\r\n\r\nexport function createContext<T>(defaultValue: T): Context<T> {\r\n const contextId = Symbol('context');\r\n \r\n const Provider: ComponentType<{ value: T; children?: any }> = ({ value, children }) => {\r\n contexts.set(contextId, value);\r\n return children;\r\n };\r\n \r\n const Consumer: ComponentType<{ children: (value: T) => any }> = ({ children }) => {\r\n const value = contexts.get(contextId) ?? defaultValue;\r\n return children(value);\r\n };\r\n\r\n const context: Context<T> = {\r\n Provider,\r\n Consumer,\r\n displayName: 'Context'\r\n };\r\n\r\n return context;\r\n}\r\n\r\nexport function useContext<T>(context: Context<T>): T {\r\n // In a real implementation, this would access the context value from the component tree\r\n // For now, returning a default value to satisfy TypeScript\r\n return {} as T;\r\n}\r\n\r\n// Export the Context type for external use\r\nexport type { Context } from './types.js';\r\n","import { createElement } from './jsx-runtime.js';\r\nimport { prepareRender, finishRender, setRenderCallback } from './hooks.js';\r\nimport type { ReactElement } from './types.js';\r\n\r\n// Render function for client-side rendering\r\nexport async function render(element: ReactElement, container: Element | DocumentFragment): Promise<void> {\r\n try {\r\n // Set up render callback for state updates\r\n setRenderCallback(render, element, container);\r\n \r\n // Prepare render context\r\n const renderId = prepareRender(element);\r\n \r\n // Create DOM nodes from virtual elements\r\n const domNode = await createElement(element);\r\n \r\n // Clear container and append new content\r\n // Handle both Element and DocumentFragment cases\r\n if (container instanceof Element) {\r\n container.innerHTML = '';\r\n } else {\r\n // For DocumentFragment, clear all children\r\n while (container.firstChild) {\r\n container.removeChild(container.firstChild);\r\n }\r\n }\r\n container.appendChild(domNode);\r\n \r\n // Finish render\r\n finishRender();\r\n \r\n console.log('Render completed successfully');\r\n } catch (error) {\r\n console.error('Error during render:', error);\r\n // Clean up render context on error\r\n finishRender();\r\n throw error;\r\n }\r\n}\r\n\r\n// Hydrate function for client-side hydration of SSR content\r\nexport async function hydrate(element: ReactElement, container: Element): Promise<void> {\r\n try {\r\n console.log('Starting hydration...');\r\n \r\n // For now, hydrate works the same as render\r\n // In a more advanced implementation, this would preserve existing DOM\r\n // and only attach event listeners and initialize state\r\n await render(element, container);\r\n \r\n console.log('Hydration completed successfully');\r\n } catch (error) {\r\n console.error('Error during hydration:', error);\r\n throw error;\r\n }\r\n}\r\n"],"names":["contexts","Map","async","render","element","container","prepareRender","domNode","createElement","Element","innerHTML","firstChild","removeChild","appendChild","finishRender","console","log","error","defaultValue","contextId","Symbol","Provider","value","children","set","Consumer","get","displayName","context"],"mappings":"8FAEMA,MAAeC,ICGCC,eAAAC,EAAOC,EAAuBC,GAC9C,wBAEgBF,EAAQC,EAASC,GAGlBC,gBAAcF,GAAzB,MAGAG,QAAgBC,EAAAA,cAAcJ,GAIpC,GAAIC,aAAqBI,QACvBJ,EAAUK,UAAY,QAGtB,KAAOL,EAAUM,YACLN,EAAAO,YAAYP,EAAUM,YAGpCN,EAAUQ,YAAYN,GAGTO,iBAEbC,QAAQC,IAAI,uCACLC,GAID,MAHEF,QAAAE,MAAM,uBAAwBA,GAEzBH,iBACPG,CAAA,CAEV,uBDlCO,SAA0BC,GACzB,MAAAC,EAAYC,OAAO,WAkBlB,MANqB,CAC1BC,SAX4D,EAAGC,QAAOC,eAC7DvB,EAAAwB,IAAIL,EAAWG,GACjBC,GAUPE,SAP+D,EAAGF,cAE3DA,EADOvB,EAAS0B,IAAIP,IAAcD,GAOzCS,YAAa,UAIjB,kBCiBsBzB,eAAQE,EAAuBC,GAC/C,IACFU,QAAQC,IAAI,+BAKNb,EAAOC,EAASC,GAEtBU,QAAQC,IAAI,0CACLC,GAED,MADEF,QAAAE,MAAM,0BAA2BA,GACnCA,CAAA,CAEV,sCD7BO,SAAuBW,GAG5B,MAAO,CAAC,CACV"}
|
@@ -0,0 +1,61 @@
|
|
1
|
+
import { createElement } from "./jsx-runtime.js";
|
2
|
+
import { s as setRenderCallback, p as prepareRender, f as finishRender } from "./server-renderer-C4MB-jAp.js";
|
3
|
+
const contexts = /* @__PURE__ */ new Map();
|
4
|
+
function createContext(defaultValue) {
|
5
|
+
const contextId = Symbol("context");
|
6
|
+
const Provider = ({ value, children }) => {
|
7
|
+
contexts.set(contextId, value);
|
8
|
+
return children;
|
9
|
+
};
|
10
|
+
const Consumer = ({ children }) => {
|
11
|
+
const value = contexts.get(contextId) ?? defaultValue;
|
12
|
+
return children(value);
|
13
|
+
};
|
14
|
+
const context = {
|
15
|
+
Provider,
|
16
|
+
Consumer,
|
17
|
+
displayName: "Context"
|
18
|
+
};
|
19
|
+
return context;
|
20
|
+
}
|
21
|
+
function useContext(context) {
|
22
|
+
return {};
|
23
|
+
}
|
24
|
+
async function render(element, container) {
|
25
|
+
try {
|
26
|
+
setRenderCallback(render, element, container);
|
27
|
+
const renderId = prepareRender(element);
|
28
|
+
const domNode = await createElement(element);
|
29
|
+
if (container instanceof Element) {
|
30
|
+
container.innerHTML = "";
|
31
|
+
} else {
|
32
|
+
while (container.firstChild) {
|
33
|
+
container.removeChild(container.firstChild);
|
34
|
+
}
|
35
|
+
}
|
36
|
+
container.appendChild(domNode);
|
37
|
+
finishRender();
|
38
|
+
console.log("Render completed successfully");
|
39
|
+
} catch (error) {
|
40
|
+
console.error("Error during render:", error);
|
41
|
+
finishRender();
|
42
|
+
throw error;
|
43
|
+
}
|
44
|
+
}
|
45
|
+
async function hydrate(element, container) {
|
46
|
+
try {
|
47
|
+
console.log("Starting hydration...");
|
48
|
+
await render(element, container);
|
49
|
+
console.log("Hydration completed successfully");
|
50
|
+
} catch (error) {
|
51
|
+
console.error("Error during hydration:", error);
|
52
|
+
throw error;
|
53
|
+
}
|
54
|
+
}
|
55
|
+
export {
|
56
|
+
createContext as c,
|
57
|
+
hydrate as h,
|
58
|
+
render as r,
|
59
|
+
useContext as u
|
60
|
+
};
|
61
|
+
//# sourceMappingURL=renderer-nfT7XSpo.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"renderer-nfT7XSpo.js","sources":["../src/context.ts","../src/renderer.ts"],"sourcesContent":["import type { Context, ComponentType } from './types.js';\r\n\r\nconst contexts = new Map<symbol, any>();\r\n\r\nexport function createContext<T>(defaultValue: T): Context<T> {\r\n const contextId = Symbol('context');\r\n \r\n const Provider: ComponentType<{ value: T; children?: any }> = ({ value, children }) => {\r\n contexts.set(contextId, value);\r\n return children;\r\n };\r\n \r\n const Consumer: ComponentType<{ children: (value: T) => any }> = ({ children }) => {\r\n const value = contexts.get(contextId) ?? defaultValue;\r\n return children(value);\r\n };\r\n\r\n const context: Context<T> = {\r\n Provider,\r\n Consumer,\r\n displayName: 'Context'\r\n };\r\n\r\n return context;\r\n}\r\n\r\nexport function useContext<T>(context: Context<T>): T {\r\n // In a real implementation, this would access the context value from the component tree\r\n // For now, returning a default value to satisfy TypeScript\r\n return {} as T;\r\n}\r\n\r\n// Export the Context type for external use\r\nexport type { Context } from './types.js';\r\n","import { createElement } from './jsx-runtime.js';\r\nimport { prepareRender, finishRender, setRenderCallback } from './hooks.js';\r\nimport type { ReactElement } from './types.js';\r\n\r\n// Render function for client-side rendering\r\nexport async function render(element: ReactElement, container: Element | DocumentFragment): Promise<void> {\r\n try {\r\n // Set up render callback for state updates\r\n setRenderCallback(render, element, container);\r\n \r\n // Prepare render context\r\n const renderId = prepareRender(element);\r\n \r\n // Create DOM nodes from virtual elements\r\n const domNode = await createElement(element);\r\n \r\n // Clear container and append new content\r\n // Handle both Element and DocumentFragment cases\r\n if (container instanceof Element) {\r\n container.innerHTML = '';\r\n } else {\r\n // For DocumentFragment, clear all children\r\n while (container.firstChild) {\r\n container.removeChild(container.firstChild);\r\n }\r\n }\r\n container.appendChild(domNode);\r\n \r\n // Finish render\r\n finishRender();\r\n \r\n console.log('Render completed successfully');\r\n } catch (error) {\r\n console.error('Error during render:', error);\r\n // Clean up render context on error\r\n finishRender();\r\n throw error;\r\n }\r\n}\r\n\r\n// Hydrate function for client-side hydration of SSR content\r\nexport async function hydrate(element: ReactElement, container: Element): Promise<void> {\r\n try {\r\n console.log('Starting hydration...');\r\n \r\n // For now, hydrate works the same as render\r\n // In a more advanced implementation, this would preserve existing DOM\r\n // and only attach event listeners and initialize state\r\n await render(element, container);\r\n \r\n console.log('Hydration completed successfully');\r\n } catch (error) {\r\n console.error('Error during hydration:', error);\r\n throw error;\r\n }\r\n}\r\n"],"names":[],"mappings":";;AAEA,MAAM,+BAAe,IAAiB;AAE/B,SAAS,cAAiB,cAA6B;AACtD,QAAA,YAAY,OAAO,SAAS;AAElC,QAAM,WAAwD,CAAC,EAAE,OAAO,eAAe;AAC5E,aAAA,IAAI,WAAW,KAAK;AACtB,WAAA;AAAA,EACT;AAEA,QAAM,WAA2D,CAAC,EAAE,eAAe;AACjF,UAAM,QAAQ,SAAS,IAAI,SAAS,KAAK;AACzC,WAAO,SAAS,KAAK;AAAA,EACvB;AAEA,QAAM,UAAsB;AAAA,IAC1B;AAAA,IACA;AAAA,IACA,aAAa;AAAA,EACf;AAEO,SAAA;AACT;AAEO,SAAS,WAAc,SAAwB;AAGpD,SAAO,CAAC;AACV;ACzBsB,eAAA,OAAO,SAAuB,WAAsD;AACpG,MAAA;AAEgB,sBAAA,QAAQ,SAAS,SAAS;AAGtC,UAAA,WAAW,cAAc,OAAO;AAGhC,UAAA,UAAU,MAAM,cAAc,OAAO;AAI3C,QAAI,qBAAqB,SAAS;AAChC,gBAAU,YAAY;AAAA,IAAA,OACjB;AAEL,aAAO,UAAU,YAAY;AACjB,kBAAA,YAAY,UAAU,UAAU;AAAA,MAAA;AAAA,IAC5C;AAEF,cAAU,YAAY,OAAO;AAGhB,iBAAA;AAEb,YAAQ,IAAI,+BAA+B;AAAA,WACpC,OAAO;AACN,YAAA,MAAM,wBAAwB,KAAK;AAE9B,iBAAA;AACP,UAAA;AAAA,EAAA;AAEV;AAGsB,eAAA,QAAQ,SAAuB,WAAmC;AAClF,MAAA;AACF,YAAQ,IAAI,uBAAuB;AAK7B,UAAA,OAAO,SAAS,SAAS;AAE/B,YAAQ,IAAI,kCAAkC;AAAA,WACvC,OAAO;AACN,YAAA,MAAM,2BAA2B,KAAK;AACxC,UAAA;AAAA,EAAA;AAEV;"}
|
@@ -0,0 +1,2 @@
|
|
1
|
+
"use strict";let e=!1;const t=[];function n(n){if(e)t.push(n);else{e=!0;try{for(n();t.length>0;){const e=t.shift();e?.()}}finally{e=!1}}}let r=0;const s=new Map,o=new Map,c=new Map,i=new Map,u=new Map;let a=null,l=null,f=null;function p(e=null){return r++,o.set(r,0),r}function h(){r=0}function y(t){if(!r)throw new Error("useState must be called within a render");s.has(r)||s.set(r,[]);const c=s.get(r),i=o.get(r)||0;i>=c.length&&c.push(t);const u=c[i];return o.set(r,i+1),[u,t=>{const s="function"==typeof t?t(c[i]):t;c[i]!==s&&(c[i]=s,e?n((()=>g(r))):g(r))}]}async function g(e){try{const t=c.get(e);t&&(t.forEach((e=>{e.cleanup&&e.cleanup()})),c.set(e,[])),a&&l&&f&&await a(f,l)}catch(t){console.error("Error during rerender:",t)}}async function w(e){if(null==e||"boolean"==typeof e)return"";if("string"==typeof e||"number"==typeof e)return d(String(e));if(Array.isArray(e)){return(await Promise.all(e.map((e=>w(e))))).join("")}if(e&&"object"==typeof e&&"type"in e){const{type:n,props:r={}}=e;if("function"==typeof n)try{const e=await n(r);return await w(e)}catch(t){return console.error("Error rendering component:",t),`\x3c!-- Error rendering component: ${t.message} --\x3e`}if("string"==typeof n)return await async function(e,t){const{children:n,...r}=t,s=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]),o=Object.entries(r).filter((([e,t])=>!e.startsWith("on")&&"key"!==e&&"ref"!==e&&(null!=t&&!1!==t))).map((([e,t])=>{if("className"===e&&(e="class"),!0===t)return e;if("style"===e&&"object"==typeof t&&null!==t){return`style="${d(Object.entries(t).map((([e,t])=>{return`${n=e,n.replace(/[A-Z]/g,(e=>`-${e.toLowerCase()}`))}:${t}`;var n})).join(";"))}"`}return`${e}="${d(String(t))}"`})).join(" "),c=`<${e}${o?" "+o:""}>`;if(s.has(e))return c.slice(0,-1)+"/>";const i=`</${e}>`;if(null!=n){return c+await w(n)+i}return c+i}(n,r)}return d("object"==typeof e?JSON.stringify(e):String(e))}function d(e){const t={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"};return e.replace(/[&<>"'/]/g,(e=>t[e]))}exports.batchUpdates=n,exports.finishRender=h,exports.prepareRender=p,exports.renderToString=async function(e){p(!0);try{return await w(e)}finally{h()}},exports.setRenderCallback=function(e,t,n){a=e,l=n,f=t},exports.useEffect=function(e,t){if(!r)throw new Error("useEffect must be called within a render");const n=o.get(r)||0;c.has(r)||c.set(r,[]);const s=c.get(r),i=s[n];i&&t&&i.deps&&!t.some(((e,t)=>e!==i.deps[t]))||(i?.cleanup&&i.cleanup(),queueMicrotask((()=>{const r=e()||void 0;s[n]={cleanup:r,deps:t||[]}}))),o.set(r,n+1)},exports.useErrorBoundary=function(){const[e,t]=y(null);return[e,()=>t(null)]},exports.useMemo=function(e,t){if(!r)throw new Error("useMemo must be called within a render");const n=o.get(r)||0;i.has(r)||i.set(r,[]);const s=i.get(r),c=s[n];if(!c||t&&t.some(((e,t)=>!Object.is(e,c.deps[t])))){const c=e();return s[n]={value:c,deps:t||[]},o.set(r,n+1),c}return o.set(r,n+1),c.value},exports.useRef=function(e){if(!r)throw new Error("useRef must be called within a render");const t=o.get(r)||0;u.has(r)||u.set(r,[]);const n=u.get(r);if(t>=n.length){const s={current:e};return n.push(s),o.set(r,t+1),s}const s=n[t];return o.set(r,t+1),s},exports.useState=y;
|
2
|
+
//# sourceMappingURL=server-renderer-B5b0Q0ck.cjs.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"server-renderer-B5b0Q0ck.cjs","sources":["../src/batch.ts","../src/hooks.ts","../src/server-renderer.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 { batchUpdates, isBatching } from './batch.js';\r\n\r\n// Current render ID counter\r\nlet currentRender = 0;\r\nlet renderContext: any = null;\r\n\r\n// State storage\r\nconst states = new Map<number, any[]>();\r\nconst stateIndices = new Map<number, number>();\r\nconst effects = new Map<number, any[]>();\r\nconst memos = new Map<number, any[]>();\r\nconst refs = new Map<number, any[]>();\r\n\r\n// Rendering callbacks\r\nlet globalRenderCallback: any = null;\r\nlet globalContainer: any = null;\r\nlet currentElement: any = null;\r\n\r\nexport function setRenderCallback(callback: any, element: any, container: any): void {\r\n globalRenderCallback = callback;\r\n globalContainer = container;\r\n currentElement = element;\r\n}\r\n\r\nexport function prepareRender(component: any = null): number {\r\n currentRender++;\r\n renderContext = component;\r\n stateIndices.set(currentRender, 0);\r\n return currentRender;\r\n}\r\n\r\nexport function finishRender(): void {\r\n renderContext = null;\r\n currentRender = 0;\r\n}\r\n\r\nexport function useState<T>(initial: T): [T, (newValue: 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 (!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) || 0;\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 \r\n const setState = (newValue: T | ((prev: T) => T)) => {\r\n const nextValue = typeof newValue === 'function'\r\n ? (newValue as ((prev: T) => T))(componentStates[index])\r\n : newValue;\r\n \r\n if (componentStates[index] === nextValue) return;\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[]): void {\r\n if (!currentRender) throw new Error(\"useEffect must be called within a render\");\r\n\r\n const effectIndex = stateIndices.get(currentRender) || 0;\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 if (!prevEffect || !deps || !prevEffect.deps || deps.some((dep, i) => dep !== prevEffect.deps[i])) {\r\n if (prevEffect?.cleanup) {\r\n prevEffect.cleanup();\r\n }\r\n \r\n // Schedule effect execution after render is complete\r\n queueMicrotask(() => {\r\n const cleanup = callback() || undefined;\r\n componentEffects[effectIndex] = { cleanup, deps: 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) || 0;\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: 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): { current: T } {\r\n if (!currentRender) throw new Error(\"useRef must be called within a render\");\r\n \r\n const refIndex = stateIndices.get(currentRender) || 0;\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 \r\n if (refIndex >= componentRefs.length) {\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\nasync function rerender(rendererId: number): Promise<void> {\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 // Trigger re-render\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\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// Re-export from context\r\nexport { createContext, useContext } from './context.js';\r\n","import { VNode } from './types.js';\r\nimport { prepareRender, finishRender } from './hooks.js';\r\n\r\nexport async function renderToString(element: any): Promise<string> {\r\n const renderId = prepareRender(true); // Mark as SSR\r\n \r\n try {\r\n const html = await renderNodeToString(element);\r\n return html;\r\n } finally {\r\n finishRender();\r\n }\r\n}\r\n\r\nasync function renderNodeToString(node: any): Promise<string> {\r\n // Handle null, undefined, boolean\r\n if (node == null || typeof node === 'boolean') {\r\n return '';\r\n }\r\n\r\n // Handle primitives\r\n if (typeof node === 'string' || typeof node === 'number') {\r\n return escapeHtml(String(node));\r\n }\r\n\r\n // Handle arrays\r\n if (Array.isArray(node)) {\r\n const results = await Promise.all(node.map(child => renderNodeToString(child)));\r\n return results.join('');\r\n }\r\n\r\n // Handle objects with type and props (React-like elements)\r\n if (node && typeof node === 'object' && 'type' in node) {\r\n const { type, props = {} } = node;\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 return await renderNodeToString(result);\r\n } catch (error:any) {\r\n console.error('Error rendering component:', error);\r\n return `<!-- Error rendering component: ${error.message} -->`;\r\n }\r\n }\r\n\r\n // Handle DOM elements\r\n if (typeof type === 'string') {\r\n return await renderDOMElement(type, props);\r\n }\r\n }\r\n\r\n // Fallback for other objects\r\n if (typeof node === 'object') {\r\n return escapeHtml(JSON.stringify(node));\r\n }\r\n\r\n return escapeHtml(String(node));\r\n}\r\n\r\nasync function renderDOMElement(tagName: string, props: any): Promise<string> {\r\n const { children, ...attrs } = props;\r\n \r\n // Self-closing tags\r\n const voidElements = new Set([\r\n 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',\r\n 'link', 'meta', 'param', 'source', 'track', 'wbr'\r\n ]);\r\n\r\n // Build attributes string\r\n const attributeString = Object.entries(attrs)\r\n .filter(([key, value]) => {\r\n // Filter out React-specific props and event handlers\r\n if (key.startsWith('on') || key === 'key' || key === 'ref') return false;\r\n if (value == null || value === false) return false;\r\n return true;\r\n })\r\n .map(([key, value]) => {\r\n // Handle className -> class\r\n if (key === 'className') key = 'class';\r\n \r\n // Handle boolean attributes\r\n if (value === true) return key;\r\n \r\n // Handle style objects\r\n if (key === 'style' && typeof value === 'object' && value !== null) {\r\n const styleString = Object.entries(value)\r\n .map(([prop, val]) => `${kebabCase(prop)}:${val}`)\r\n .join(';');\r\n return `style=\"${escapeHtml(styleString)}\"`;\r\n }\r\n \r\n return `${key}=\"${escapeHtml(String(value))}\"`;\r\n })\r\n .join(' ');\r\n\r\n const openTag = `<${tagName}${attributeString ? ' ' + attributeString : ''}>`;\r\n \r\n // Self-closing elements\r\n if (voidElements.has(tagName)) {\r\n return openTag.slice(0, -1) + '/>';\r\n }\r\n\r\n // Elements with children\r\n const closeTag = `</${tagName}>`;\r\n \r\n if (children != null) {\r\n const childrenString = await renderNodeToString(children);\r\n return openTag + childrenString + closeTag;\r\n }\r\n \r\n return openTag + closeTag;\r\n}\r\n\r\nfunction escapeHtml(text: string): string {\r\n const htmlEscapes: Record<string, string> = {\r\n '&': '&',\r\n '<': '<',\r\n '>': '>',\r\n '\"': '"',\r\n \"'\": ''',\r\n '/': '/'\r\n };\r\n \r\n return text.replace(/[&<>\"'/]/g, (match) => htmlEscapes[match]);\r\n}\r\n\r\nfunction kebabCase(str: string): string {\r\n return str.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);\r\n}\r\n"],"names":["isBatching","queue","batchUpdates","fn","push","length","nextFn","shift","currentRender","states","Map","stateIndices","effects","memos","refs","globalRenderCallback","globalContainer","currentElement","prepareRender","component","set","finishRender","useState","initial","Error","has","componentStates","get","index","state","newValue","nextValue","rerender","async","rendererId","componentEffects","forEach","effect","cleanup","error","console","renderNodeToString","node","escapeHtml","String","Array","isArray","Promise","all","map","child","join","type","props","result","message","tagName","children","attrs","voidElements","Set","attributeString","Object","entries","filter","key","value","startsWith","prop","val","str","replace","match","toLowerCase","openTag","slice","closeTag","renderDOMElement","JSON","stringify","text","htmlEscapes","element","callback","container","deps","effectIndex","prevEffect","some","dep","i","queueMicrotask","setError","factory","memoIndex","componentMemos","prevMemo","is","refIndex","componentRefs","ref","current"],"mappings":"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"}
|
@@ -16,18 +16,12 @@ function batchUpdates(fn) {
|
|
16
16
|
isBatching = false;
|
17
17
|
}
|
18
18
|
}
|
19
|
-
function getIsBatching() {
|
20
|
-
return isBatching;
|
21
|
-
}
|
22
19
|
let currentRender = 0;
|
23
|
-
let isServerRender = false;
|
24
20
|
const states = /* @__PURE__ */ new Map();
|
25
21
|
const stateIndices = /* @__PURE__ */ new Map();
|
26
22
|
const effects = /* @__PURE__ */ new Map();
|
27
23
|
const memos = /* @__PURE__ */ new Map();
|
28
24
|
const refs = /* @__PURE__ */ new Map();
|
29
|
-
const isServer = typeof window === "undefined";
|
30
|
-
const serverStates = /* @__PURE__ */ new Map();
|
31
25
|
let globalRenderCallback = null;
|
32
26
|
let globalContainer = null;
|
33
27
|
let currentElement = null;
|
@@ -36,38 +30,18 @@ function setRenderCallback(callback, element, container) {
|
|
36
30
|
globalContainer = container;
|
37
31
|
currentElement = element;
|
38
32
|
}
|
39
|
-
function prepareRender(
|
33
|
+
function prepareRender(component = null) {
|
40
34
|
currentRender++;
|
41
|
-
isServerRender = isSSR;
|
42
35
|
stateIndices.set(currentRender, 0);
|
43
36
|
return currentRender;
|
44
37
|
}
|
45
38
|
function finishRender() {
|
46
|
-
if (isServer || isServerRender) {
|
47
|
-
serverStates.delete(currentRender);
|
48
|
-
}
|
49
|
-
isServerRender = false;
|
50
39
|
currentRender = 0;
|
51
40
|
}
|
52
41
|
function useState(initial) {
|
53
|
-
if (!currentRender
|
42
|
+
if (!currentRender) {
|
54
43
|
throw new Error("useState must be called within a render");
|
55
44
|
}
|
56
|
-
if (isServer || isServerRender) {
|
57
|
-
if (!serverStates.has(currentRender)) {
|
58
|
-
serverStates.set(currentRender, /* @__PURE__ */ new Map());
|
59
|
-
}
|
60
|
-
const componentState = serverStates.get(currentRender);
|
61
|
-
const index2 = stateIndices.get(currentRender) || 0;
|
62
|
-
if (!componentState.has(index2)) {
|
63
|
-
componentState.set(index2, initial);
|
64
|
-
}
|
65
|
-
const state2 = componentState.get(index2);
|
66
|
-
const setState2 = (_newValue) => {
|
67
|
-
};
|
68
|
-
stateIndices.set(currentRender, index2 + 1);
|
69
|
-
return [state2, setState2];
|
70
|
-
}
|
71
45
|
if (!states.has(currentRender)) {
|
72
46
|
states.set(currentRender, []);
|
73
47
|
}
|
@@ -91,12 +65,7 @@ function useState(initial) {
|
|
91
65
|
return [state, setState];
|
92
66
|
}
|
93
67
|
function useEffect(callback, deps) {
|
94
|
-
if (!currentRender
|
95
|
-
if (isServer || isServerRender) {
|
96
|
-
const effectIndex2 = stateIndices.get(currentRender) || 0;
|
97
|
-
stateIndices.set(currentRender, effectIndex2 + 1);
|
98
|
-
return;
|
99
|
-
}
|
68
|
+
if (!currentRender) throw new Error("useEffect must be called within a render");
|
100
69
|
const effectIndex = stateIndices.get(currentRender) || 0;
|
101
70
|
if (!effects.has(currentRender)) {
|
102
71
|
effects.set(currentRender, []);
|
@@ -115,7 +84,7 @@ function useEffect(callback, deps) {
|
|
115
84
|
stateIndices.set(currentRender, effectIndex + 1);
|
116
85
|
}
|
117
86
|
function useMemo(factory, deps) {
|
118
|
-
if (!currentRender
|
87
|
+
if (!currentRender) throw new Error("useMemo must be called within a render");
|
119
88
|
const memoIndex = stateIndices.get(currentRender) || 0;
|
120
89
|
if (!memos.has(currentRender)) {
|
121
90
|
memos.set(currentRender, []);
|
@@ -132,7 +101,7 @@ function useMemo(factory, deps) {
|
|
132
101
|
return prevMemo.value;
|
133
102
|
}
|
134
103
|
function useRef(initial) {
|
135
|
-
if (!currentRender
|
104
|
+
if (!currentRender) throw new Error("useRef must be called within a render");
|
136
105
|
const refIndex = stateIndices.get(currentRender) || 0;
|
137
106
|
if (!refs.has(currentRender)) {
|
138
107
|
refs.set(currentRender, []);
|
@@ -271,11 +240,9 @@ export {
|
|
271
240
|
useEffect as d,
|
272
241
|
useState as e,
|
273
242
|
finishRender as f,
|
274
|
-
getIsBatching as g,
|
275
|
-
isBatching as i,
|
276
243
|
prepareRender as p,
|
277
244
|
renderToString as r,
|
278
245
|
setRenderCallback as s,
|
279
246
|
useErrorBoundary as u
|
280
247
|
};
|
281
|
-
//# sourceMappingURL=server-renderer-
|
248
|
+
//# sourceMappingURL=server-renderer-C4MB-jAp.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"server-renderer-C4MB-jAp.js","sources":["../src/batch.ts","../src/hooks.ts","../src/server-renderer.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 { batchUpdates, isBatching } from './batch.js';\r\n\r\n// Current render ID counter\r\nlet currentRender = 0;\r\nlet renderContext: any = null;\r\n\r\n// State storage\r\nconst states = new Map<number, any[]>();\r\nconst stateIndices = new Map<number, number>();\r\nconst effects = new Map<number, any[]>();\r\nconst memos = new Map<number, any[]>();\r\nconst refs = new Map<number, any[]>();\r\n\r\n// Rendering callbacks\r\nlet globalRenderCallback: any = null;\r\nlet globalContainer: any = null;\r\nlet currentElement: any = null;\r\n\r\nexport function setRenderCallback(callback: any, element: any, container: any): void {\r\n globalRenderCallback = callback;\r\n globalContainer = container;\r\n currentElement = element;\r\n}\r\n\r\nexport function prepareRender(component: any = null): number {\r\n currentRender++;\r\n renderContext = component;\r\n stateIndices.set(currentRender, 0);\r\n return currentRender;\r\n}\r\n\r\nexport function finishRender(): void {\r\n renderContext = null;\r\n currentRender = 0;\r\n}\r\n\r\nexport function useState<T>(initial: T): [T, (newValue: 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 (!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) || 0;\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 \r\n const setState = (newValue: T | ((prev: T) => T)) => {\r\n const nextValue = typeof newValue === 'function'\r\n ? (newValue as ((prev: T) => T))(componentStates[index])\r\n : newValue;\r\n \r\n if (componentStates[index] === nextValue) return;\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[]): void {\r\n if (!currentRender) throw new Error(\"useEffect must be called within a render\");\r\n\r\n const effectIndex = stateIndices.get(currentRender) || 0;\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 if (!prevEffect || !deps || !prevEffect.deps || deps.some((dep, i) => dep !== prevEffect.deps[i])) {\r\n if (prevEffect?.cleanup) {\r\n prevEffect.cleanup();\r\n }\r\n \r\n // Schedule effect execution after render is complete\r\n queueMicrotask(() => {\r\n const cleanup = callback() || undefined;\r\n componentEffects[effectIndex] = { cleanup, deps: 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) || 0;\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: 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): { current: T } {\r\n if (!currentRender) throw new Error(\"useRef must be called within a render\");\r\n \r\n const refIndex = stateIndices.get(currentRender) || 0;\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 \r\n if (refIndex >= componentRefs.length) {\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\nasync function rerender(rendererId: number): Promise<void> {\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 // Trigger re-render\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\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// Re-export from context\r\nexport { createContext, useContext } from './context.js';\r\n","import { VNode } from './types.js';\r\nimport { prepareRender, finishRender } from './hooks.js';\r\n\r\nexport async function renderToString(element: any): Promise<string> {\r\n const renderId = prepareRender(true); // Mark as SSR\r\n \r\n try {\r\n const html = await renderNodeToString(element);\r\n return html;\r\n } finally {\r\n finishRender();\r\n }\r\n}\r\n\r\nasync function renderNodeToString(node: any): Promise<string> {\r\n // Handle null, undefined, boolean\r\n if (node == null || typeof node === 'boolean') {\r\n return '';\r\n }\r\n\r\n // Handle primitives\r\n if (typeof node === 'string' || typeof node === 'number') {\r\n return escapeHtml(String(node));\r\n }\r\n\r\n // Handle arrays\r\n if (Array.isArray(node)) {\r\n const results = await Promise.all(node.map(child => renderNodeToString(child)));\r\n return results.join('');\r\n }\r\n\r\n // Handle objects with type and props (React-like elements)\r\n if (node && typeof node === 'object' && 'type' in node) {\r\n const { type, props = {} } = node;\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 return await renderNodeToString(result);\r\n } catch (error:any) {\r\n console.error('Error rendering component:', error);\r\n return `<!-- Error rendering component: ${error.message} -->`;\r\n }\r\n }\r\n\r\n // Handle DOM elements\r\n if (typeof type === 'string') {\r\n return await renderDOMElement(type, props);\r\n }\r\n }\r\n\r\n // Fallback for other objects\r\n if (typeof node === 'object') {\r\n return escapeHtml(JSON.stringify(node));\r\n }\r\n\r\n return escapeHtml(String(node));\r\n}\r\n\r\nasync function renderDOMElement(tagName: string, props: any): Promise<string> {\r\n const { children, ...attrs } = props;\r\n \r\n // Self-closing tags\r\n const voidElements = new Set([\r\n 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',\r\n 'link', 'meta', 'param', 'source', 'track', 'wbr'\r\n ]);\r\n\r\n // Build attributes string\r\n const attributeString = Object.entries(attrs)\r\n .filter(([key, value]) => {\r\n // Filter out React-specific props and event handlers\r\n if (key.startsWith('on') || key === 'key' || key === 'ref') return false;\r\n if (value == null || value === false) return false;\r\n return true;\r\n })\r\n .map(([key, value]) => {\r\n // Handle className -> class\r\n if (key === 'className') key = 'class';\r\n \r\n // Handle boolean attributes\r\n if (value === true) return key;\r\n \r\n // Handle style objects\r\n if (key === 'style' && typeof value === 'object' && value !== null) {\r\n const styleString = Object.entries(value)\r\n .map(([prop, val]) => `${kebabCase(prop)}:${val}`)\r\n .join(';');\r\n return `style=\"${escapeHtml(styleString)}\"`;\r\n }\r\n \r\n return `${key}=\"${escapeHtml(String(value))}\"`;\r\n })\r\n .join(' ');\r\n\r\n const openTag = `<${tagName}${attributeString ? ' ' + attributeString : ''}>`;\r\n \r\n // Self-closing elements\r\n if (voidElements.has(tagName)) {\r\n return openTag.slice(0, -1) + '/>';\r\n }\r\n\r\n // Elements with children\r\n const closeTag = `</${tagName}>`;\r\n \r\n if (children != null) {\r\n const childrenString = await renderNodeToString(children);\r\n return openTag + childrenString + closeTag;\r\n }\r\n \r\n return openTag + closeTag;\r\n}\r\n\r\nfunction escapeHtml(text: string): string {\r\n const htmlEscapes: Record<string, string> = {\r\n '&': '&',\r\n '<': '<',\r\n '>': '>',\r\n '\"': '"',\r\n \"'\": ''',\r\n '/': '/'\r\n };\r\n \r\n return text.replace(/[&<>\"'/]/g, (match) => htmlEscapes[match]);\r\n}\r\n\r\nfunction kebabCase(str: string): string {\r\n return str.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);\r\n}\r\n"],"names":["ref"],"mappings":"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"}
|
package/dist/server-renderer.cjs
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./server-renderer-
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./server-renderer-B5b0Q0ck.cjs");exports.renderToString=e.renderToString;
|
2
2
|
//# sourceMappingURL=server-renderer.cjs.map
|
package/dist/server-renderer.js
CHANGED
package/package.json
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["build.js"],"names":[],"mappings":""}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["dev.js"],"names":[],"mappings":""}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"esbuild.config.d.ts","sourceRoot":"","sources":["esbuild.config.js"],"names":[],"mappings":""}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"postcss.config.d.ts","sourceRoot":"","sources":["postcss.config.js"],"names":[],"mappings":""}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["server.js"],"names":[],"mappings":""}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"App.d.ts","sourceRoot":"","sources":["App.tsx"],"names":[],"mappings":"AAwFA,wBAAgB,GAAG,gBA2TlB"}
|
@@ -0,0 +1,148 @@
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "frontend-hamroun/jsx-runtime";
|
2
|
+
import { useState, useEffect, useMemo, useErrorBoundary, useForm } from 'frontend-hamroun';
|
3
|
+
// Todo Item Component
|
4
|
+
function TodoItem({ todo, onToggle, onDelete }) {
|
5
|
+
return (_jsxs("div", { className: `todo-item ${todo.completed ? 'completed' : ''}`, children: [_jsx("input", { type: "checkbox", checked: todo.completed, onChange: () => onToggle(todo.id), className: "todo-checkbox" }), _jsx("span", { className: "todo-text", children: todo.text }), _jsxs("span", { className: `todo-priority priority-${todo.priority}`, children: [todo.priority === 'high' && '🔴', todo.priority === 'medium' && '🟡', todo.priority === 'low' && '🟢', todo.priority] }), _jsx("div", { className: "todo-actions", children: _jsx("button", { onClick: () => onDelete(todo.id), className: "btn btn-sm btn-danger", title: "Delete todo", children: "\uD83D\uDDD1\uFE0F" }) })] }));
|
6
|
+
}
|
7
|
+
// Theme Toggle Button Component
|
8
|
+
function ThemeToggleButton({ theme, onToggle }) {
|
9
|
+
return (_jsx("button", { onClick: onToggle, className: "btn btn-secondary theme-toggle", title: "Toggle theme", children: theme === 'light' ? '🌙' : '☀️' }));
|
10
|
+
}
|
11
|
+
// App Footer Component
|
12
|
+
function AppFooter({ theme }) {
|
13
|
+
return (_jsx("footer", { className: "app-footer", children: _jsxs("div", { className: "container", children: [_jsx("p", { children: "Built with Frontend Hamroun \u2022 Hooks: useState, useEffect, useMemo, useErrorBoundary, useForm" }), _jsxs("p", { children: ["Theme: ", _jsx("strong", { children: theme }), " \u2022 Environment: ", _jsx("strong", { children: "Client" })] })] }) }));
|
14
|
+
}
|
15
|
+
export function App() {
|
16
|
+
// State hooks with proper typing
|
17
|
+
const [todos, setTodos] = useState([
|
18
|
+
{ id: 1, text: 'Learn Frontend Hamroun hooks', completed: false, priority: 'high' },
|
19
|
+
{ id: 2, text: 'Build a todo app', completed: false, priority: 'medium' },
|
20
|
+
{ id: 3, text: 'Master client-side concepts', completed: true, priority: 'low' }
|
21
|
+
]);
|
22
|
+
const [theme, setTheme] = useState('light');
|
23
|
+
const [taskFilter, setTaskFilter] = useState('all');
|
24
|
+
// Error boundary hook
|
25
|
+
const [error, resetError] = useErrorBoundary();
|
26
|
+
// Form hook for adding new todos
|
27
|
+
const addTodoForm = useForm({
|
28
|
+
initialValues: {
|
29
|
+
text: '',
|
30
|
+
priority: 'medium'
|
31
|
+
},
|
32
|
+
validate: (values) => {
|
33
|
+
const errors = {};
|
34
|
+
if (!values.text.trim()) {
|
35
|
+
errors.text = 'Todo text is required';
|
36
|
+
}
|
37
|
+
return errors;
|
38
|
+
},
|
39
|
+
onSubmit: (values) => {
|
40
|
+
const newTodo = {
|
41
|
+
id: Date.now(),
|
42
|
+
text: values.text.trim(),
|
43
|
+
completed: false,
|
44
|
+
priority: values.priority
|
45
|
+
};
|
46
|
+
setTodos(prev => [...prev, newTodo]);
|
47
|
+
addTodoForm.resetForm();
|
48
|
+
}
|
49
|
+
});
|
50
|
+
// Mount effect
|
51
|
+
useEffect(() => {
|
52
|
+
console.log('Todo App mounted');
|
53
|
+
// Load saved todos from localStorage
|
54
|
+
const savedTodos = localStorage.getItem('todos');
|
55
|
+
const savedTheme = localStorage.getItem('theme');
|
56
|
+
if (savedTodos) {
|
57
|
+
try {
|
58
|
+
setTodos(JSON.parse(savedTodos));
|
59
|
+
}
|
60
|
+
catch (e) {
|
61
|
+
console.error('Failed to load saved todos');
|
62
|
+
}
|
63
|
+
}
|
64
|
+
if (savedTheme) {
|
65
|
+
setTheme(savedTheme);
|
66
|
+
}
|
67
|
+
return () => {
|
68
|
+
console.log('Todo App unmounting');
|
69
|
+
};
|
70
|
+
}, []);
|
71
|
+
// Theme effect
|
72
|
+
useEffect(() => {
|
73
|
+
document.body.className = `theme-${theme}`;
|
74
|
+
document.documentElement.setAttribute('data-theme', theme);
|
75
|
+
localStorage.setItem('theme', theme);
|
76
|
+
}, [theme]);
|
77
|
+
// Save todos effect
|
78
|
+
useEffect(() => {
|
79
|
+
if (todos.length === 0)
|
80
|
+
return;
|
81
|
+
localStorage.setItem('todos', JSON.stringify(todos));
|
82
|
+
}, [todos]);
|
83
|
+
// Memoized filtered todos
|
84
|
+
const filteredTodos = useMemo(() => {
|
85
|
+
console.log('Filtering todos - memoized computation');
|
86
|
+
return todos.filter(todo => {
|
87
|
+
if (taskFilter === 'completed')
|
88
|
+
return todo.completed;
|
89
|
+
if (taskFilter === 'active')
|
90
|
+
return !todo.completed;
|
91
|
+
if (taskFilter === 'high')
|
92
|
+
return todo.priority === 'high';
|
93
|
+
if (taskFilter === 'medium')
|
94
|
+
return todo.priority === 'medium';
|
95
|
+
if (taskFilter === 'low')
|
96
|
+
return todo.priority === 'low';
|
97
|
+
return true;
|
98
|
+
});
|
99
|
+
}, [todos, taskFilter]);
|
100
|
+
// Memoized todo stats
|
101
|
+
const todoStats = useMemo(() => {
|
102
|
+
const total = todos.length;
|
103
|
+
const completed = todos.filter(t => t.completed).length;
|
104
|
+
const active = total - completed;
|
105
|
+
const highPriority = todos.filter(t => t.priority === 'high' && !t.completed).length;
|
106
|
+
return { total, completed, active, highPriority };
|
107
|
+
}, [todos]);
|
108
|
+
// Todo action handlers
|
109
|
+
const handleToggleTodo = (id) => {
|
110
|
+
console.log('Toggling todo:', id);
|
111
|
+
setTodos(prev => prev.map(todo => todo.id === id ? { ...todo, completed: !todo.completed } : todo));
|
112
|
+
};
|
113
|
+
const handleDeleteTodo = (id) => {
|
114
|
+
console.log('Deleting todo:', id);
|
115
|
+
setTodos(prev => prev.filter(todo => todo.id !== id));
|
116
|
+
};
|
117
|
+
// Event handlers
|
118
|
+
const clearCompleted = () => {
|
119
|
+
setTodos(prev => prev.filter(todo => !todo.completed));
|
120
|
+
};
|
121
|
+
const markAllComplete = () => {
|
122
|
+
setTodos(prev => prev.map(todo => ({ ...todo, completed: true })));
|
123
|
+
};
|
124
|
+
const toggleTheme = () => {
|
125
|
+
setTheme(prev => prev === 'light' ? 'dark' : 'light');
|
126
|
+
};
|
127
|
+
const simulateError = () => {
|
128
|
+
throw new Error('Simulated error for testing error boundary');
|
129
|
+
};
|
130
|
+
if (error) {
|
131
|
+
return (_jsxs("div", { className: "error-container", children: [_jsx("h2", { children: "Something went wrong!" }), _jsx("p", { children: error.message }), _jsx("button", { onClick: resetError, className: "btn btn-primary", children: "Try Again" })] }));
|
132
|
+
}
|
133
|
+
return (_jsxs("div", { className: `app theme-${theme}`, children: [_jsx("header", { className: "app-header", children: _jsxs("div", { className: "container", children: [_jsxs("h1", { className: "app-title", children: ["\uD83D\uDCDD Todo App", _jsx("span", { className: "subtitle", children: "Built with Frontend Hamroun" })] }), _jsxs("div", { className: "header-controls", children: [_jsx(ThemeToggleButton, { theme: theme, onToggle: toggleTheme }), _jsxs("div", { className: "stats", children: [_jsxs("span", { className: "stat", children: ["Total: ", _jsx("strong", { children: todoStats.total })] }), _jsxs("span", { className: "stat", children: ["Active: ", _jsx("strong", { children: todoStats.active })] }), _jsxs("span", { className: "stat", children: ["Done: ", _jsx("strong", { children: todoStats.completed })] })] })] })] }) }), _jsx("main", { className: "main-content", children: _jsxs("div", { className: "container", children: [_jsxs("section", { className: "card add-todo-section", children: [_jsx("h2", { children: "\u2795 Add New Todo" }), _jsxs("form", { onSubmit: addTodoForm.handleSubmit, className: "add-todo-form", children: [_jsx("input", { type: "text", name: "text", value: addTodoForm.values.text, onChange: addTodoForm.handleChange, onBlur: addTodoForm.handleBlur, placeholder: "What needs to be done?", className: `input todo-input ${addTodoForm.errors.text && addTodoForm.touched.text ? 'error' : ''}` }), _jsxs("select", { name: "priority", value: addTodoForm.values.priority, onChange: addTodoForm.handleChange, className: "select priority-select", children: [_jsx("option", { value: "low", children: "\uD83D\uDFE2 Low Priority" }), _jsx("option", { value: "medium", children: "\uD83D\uDFE1 Medium Priority" }), _jsx("option", { value: "high", children: "\uD83D\uDD34 High Priority" })] }), _jsx("button", { type: "submit", className: `btn btn-primary add-btn ${addTodoForm.isSubmitting ? 'loading' : ''}`, disabled: addTodoForm.isSubmitting || !addTodoForm.values.text.trim(), children: addTodoForm.isSubmitting ? '⏳ Adding...' : '➕ Add Todo' })] }), addTodoForm.errors.text && addTodoForm.touched.text && (_jsx("div", { className: "error-message", children: addTodoForm.errors.text }))] }), _jsxs("section", { className: "card filters-section", children: [_jsx("h2", { children: "\uD83D\uDD0D Filter Todos" }), _jsx("div", { className: "filters", children: ['all', 'active', 'completed', 'high', 'medium', 'low'].map(filterType => (_jsxs("button", { onClick: () => setTaskFilter(filterType), className: `btn btn-sm filter-btn ${taskFilter === filterType ? 'btn-primary' : 'btn-outline'}`, children: [filterType === 'all' && '📋 All', filterType === 'active' && '⏳ Active', filterType === 'completed' && '✅ Completed', filterType === 'high' && '🔴 High Priority', filterType === 'medium' && '🟡 Medium Priority', filterType === 'low' && '🟢 Low Priority'] }, filterType))) }), _jsxs("div", { className: "bulk-actions", children: [_jsx("button", { onClick: markAllComplete, className: "btn btn-success btn-sm", disabled: todoStats.active === 0, children: "\u2705 Mark All Complete" }), _jsx("button", { onClick: clearCompleted, className: "btn btn-warning btn-sm", disabled: todoStats.completed === 0, children: "\uD83D\uDDD1\uFE0F Clear Completed" })] })] }), _jsxs("section", { className: "card todos-section", children: [_jsxs("div", { className: "section-header", children: [_jsx("h2", { children: "\uD83D\uDCCB Todo List" }), _jsxs("div", { className: "filter-info", children: ["Showing ", _jsx("strong", { children: filteredTodos.length }), " of ", _jsx("strong", { children: todoStats.total }), " todos", taskFilter !== 'all' && _jsx("span", { className: "filter-badge", children: taskFilter })] })] }), _jsx("div", { className: "todos-list", children: filteredTodos.length > 0 ? (filteredTodos.map(todo => (_jsx(TodoItem, { todo: todo, onToggle: handleToggleTodo, onDelete: handleDeleteTodo }, todo.id)))) : (_jsx("div", { className: "empty-state", children: _jsx("p", { children: taskFilter === 'all' ? '🎉 No todos yet. Add one above!' :
|
134
|
+
taskFilter === 'completed' ? '📝 No completed todos yet.' :
|
135
|
+
taskFilter === 'active' ? '🎯 No active todos. Great job!' :
|
136
|
+
`🔍 No ${taskFilter} priority todos found.` }) })) })] }), _jsxs("section", { className: "card actions-section", children: [_jsx("h2", { children: "\u2699\uFE0F Actions" }), _jsx("div", { className: "action-buttons", children: _jsx("button", { onClick: simulateError, className: "btn btn-danger", children: "\uD83D\uDCA5 Test Error Boundary" }) })] })] }) }), _jsx(AppFooter, { theme: theme }), _jsx("style", { children: `
|
137
|
+
/* ...existing styles... */
|
138
|
+
.error-message {
|
139
|
+
color: #e74c3c;
|
140
|
+
font-size: 0.875rem;
|
141
|
+
margin-top: 0.5rem;
|
142
|
+
}
|
143
|
+
|
144
|
+
.input.error {
|
145
|
+
border-color: #e74c3c;
|
146
|
+
}
|
147
|
+
` })] }));
|
148
|
+
}
|