baraqex 1.0.74 → 2.0.0
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/CHANGELOG.md +73 -0
- package/README.md +272 -472
- package/bin/baraqex.js +150 -12
- package/dist/browser.d.ts +1 -1
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +2319 -205
- package/dist/browser.js.map +4 -4
- package/dist/core/batch.d.ts +10 -0
- package/dist/core/batch.d.ts.map +1 -0
- package/dist/core/component.d.ts +69 -0
- package/dist/core/component.d.ts.map +1 -0
- package/dist/core/context.d.ts +22 -0
- package/dist/core/context.d.ts.map +1 -0
- package/dist/core/errors.d.ts +125 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/event-bus.d.ts +29 -0
- package/dist/core/event-bus.d.ts.map +1 -0
- package/dist/core/forms.d.ts +128 -0
- package/dist/core/forms.d.ts.map +1 -0
- package/dist/core/hooks.d.ts +84 -0
- package/dist/core/hooks.d.ts.map +1 -0
- package/dist/core/index.d.ts +23 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/jsx-runtime.d.ts +45 -0
- package/dist/core/jsx-runtime.d.ts.map +1 -0
- package/dist/core/lifecycle-events.d.ts +71 -0
- package/dist/core/lifecycle-events.d.ts.map +1 -0
- package/dist/core/renderer.d.ts +39 -0
- package/dist/core/renderer.d.ts.map +1 -0
- package/dist/core/router.d.ts +108 -0
- package/dist/core/router.d.ts.map +1 -0
- package/dist/core/server-renderer.d.ts +26 -0
- package/dist/core/server-renderer.d.ts.map +1 -0
- package/dist/core/store.d.ts +102 -0
- package/dist/core/store.d.ts.map +1 -0
- package/dist/core/types.d.ts +66 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/utils.d.ts +153 -0
- package/dist/core/utils.d.ts.map +1 -0
- package/dist/core/vdom.d.ts +29 -0
- package/dist/core/vdom.d.ts.map +1 -0
- package/dist/index.d.ts +39 -35
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2592 -508
- package/dist/index.js.map +4 -4
- package/dist/server/auth.d.ts +8 -3
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/index.d.ts +2 -2
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +173 -112
- package/dist/server/index.js.map +4 -4
- package/dist/server/middleware.d.ts +10 -1
- package/dist/server/middleware.d.ts.map +1 -1
- package/package.json +224 -124
- package/templates/basic-app/src/main.jsx +336 -0
- package/templates/complete-app/api/hello.js +0 -0
- package/templates/complete-app/client.js +58 -0
- package/templates/complete-app/lib/frontend-hamroun.js +182 -0
- package/templates/complete-app/package.json +18 -0
- package/templates/complete-app/pages/about.js +119 -0
- package/templates/complete-app/pages/about.jsx +0 -0
- package/templates/complete-app/pages/index.js +157 -0
- package/templates/complete-app/pages/index.jsx +0 -0
- package/templates/complete-app/pages/wasm-demo.js +290 -0
- package/templates/complete-app/pages/wasm-demo.jsx +0 -0
- package/templates/complete-app/public/client.js +89 -0
- package/templates/complete-app/public/index.html +118 -0
- package/templates/complete-app/public/styles.css +76 -0
- package/templates/complete-app/readme.md +188 -0
- package/templates/complete-app/server.js +226 -0
- package/templates/complete-app/server.ts +275 -0
- package/templates/complete-app/src/App.tsx +59 -0
- package/templates/complete-app/src/client.ts +61 -0
- package/templates/complete-app/src/client.tsx +18 -0
- package/templates/complete-app/src/pages/index.tsx +51 -0
- package/templates/complete-app/src/server.ts +218 -0
- package/templates/complete-app/tsconfig.json +22 -0
- package/templates/complete-app/tsconfig.server.json +19 -0
- package/templates/complete-app/vite.config.js +57 -0
- package/templates/complete-app/vite.config.ts +30 -0
- package/templates/complete-app/wasm/build.bat +0 -0
- package/templates/complete-app/wasm/build.sh +0 -0
- package/templates/complete-app/wasm/example.go +0 -0
- package/templates/fullstack-app/README.md +37 -0
- package/templates/fullstack-app/api/hello.js +11 -0
- package/templates/fullstack-app/api/hello.ts +18 -0
- package/templates/fullstack-app/api/users/[id].ts +73 -0
- package/templates/fullstack-app/api/users/index.ts +32 -0
- package/templates/fullstack-app/build/main.css +874 -0
- package/templates/fullstack-app/build/main.css.map +7 -0
- package/templates/fullstack-app/build/main.js +967 -0
- package/templates/fullstack-app/build/main.js.map +7 -0
- package/templates/fullstack-app/build.ts +210 -0
- package/templates/fullstack-app/index.html +36 -0
- package/templates/fullstack-app/package.json +35 -0
- package/templates/fullstack-app/postcss.config.js +6 -0
- package/templates/fullstack-app/process-tailwind.js +45 -0
- package/templates/fullstack-app/public/_redirects +1 -0
- package/templates/fullstack-app/public/route-handler.js +47 -0
- package/templates/fullstack-app/public/spa-fix.html +17 -0
- package/templates/fullstack-app/public/styles.css +975 -0
- package/templates/fullstack-app/public/tailwind.css +15 -0
- package/templates/fullstack-app/server.js +267 -0
- package/templates/fullstack-app/server.ts +474 -0
- package/templates/fullstack-app/src/README.md +55 -0
- package/templates/fullstack-app/src/client.js +87 -0
- package/templates/fullstack-app/src/components/ClientHome.tsx +0 -0
- package/templates/fullstack-app/src/components/ErrorBoundary.tsx +36 -0
- package/templates/fullstack-app/src/components/Layout.tsx +42 -0
- package/templates/fullstack-app/src/components/StateDemo.tsx +207 -0
- package/templates/fullstack-app/src/components/UserList.tsx +44 -0
- package/templates/fullstack-app/src/config.ts +42 -0
- package/templates/fullstack-app/src/data/api.ts +206 -0
- package/templates/fullstack-app/src/main.tsx +155 -0
- package/templates/fullstack-app/src/middleware.ts +28 -0
- package/templates/fullstack-app/src/pages/404.tsx +28 -0
- package/templates/fullstack-app/src/pages/[id].tsx +0 -0
- package/templates/fullstack-app/src/pages/_app.tsx +11 -0
- package/templates/fullstack-app/src/pages/_document.tsx +25 -0
- package/templates/fullstack-app/src/pages/_error.tsx +45 -0
- package/templates/fullstack-app/src/pages/about/index.tsx +67 -0
- package/templates/fullstack-app/src/pages/about.tsx +71 -0
- package/templates/fullstack-app/src/pages/api/users/[id].ts +73 -0
- package/templates/fullstack-app/src/pages/api/users/index.ts +43 -0
- package/templates/fullstack-app/src/pages/index.tsx +110 -0
- package/templates/fullstack-app/src/pages/users/[id].tsx +153 -0
- package/templates/fullstack-app/src/pages/users.tsx +60 -0
- package/templates/fullstack-app/src/pages/wasm-demo.tsx +1 -0
- package/templates/fullstack-app/src/router.ts +255 -0
- package/templates/fullstack-app/src/styles.css +5 -0
- package/templates/fullstack-app/tailwind.config.js +11 -0
- package/templates/fullstack-app/tsconfig.json +18 -0
- package/templates/fullstack-app/vite.config.js +87 -0
- package/templates/go/build.bat +36 -0
- package/templates/go/build.sh +43 -0
- package/templates/go/example.go +205 -0
- package/templates/go-wasm-app/README.md +38 -0
- package/templates/go-wasm-app/babel.config.js +21 -0
- package/templates/go-wasm-app/build-client.js +49 -0
- package/templates/go-wasm-app/build-wasm.js +237 -0
- package/templates/go-wasm-app/build.config.js +62 -0
- package/templates/go-wasm-app/build.js +218 -0
- package/templates/go-wasm-app/package.json +32 -0
- package/templates/go-wasm-app/public/index.html +128 -0
- package/templates/go-wasm-app/public/styles.css +197 -0
- package/templates/go-wasm-app/public/wasm/example.wasm +0 -0
- package/templates/go-wasm-app/public/wasm/wasm_exec.js +561 -0
- package/templates/go-wasm-app/public/wasm/wasm_exec_node.js +39 -0
- package/templates/go-wasm-app/server.js +70 -0
- package/templates/go-wasm-app/src/App.jsx +38 -0
- package/templates/go-wasm-app/src/app.js +173 -0
- package/templates/go-wasm-app/src/client.js +57 -0
- package/templates/go-wasm-app/src/components/Footer.jsx +13 -0
- package/templates/go-wasm-app/src/components/Header.jsx +19 -0
- package/templates/go-wasm-app/src/components/WasmDemo.jsx +120 -0
- package/templates/go-wasm-app/src/main.jsx +12 -0
- package/templates/go-wasm-app/src/wasm/example.go +75 -0
- package/templates/go-wasm-app/tsconfig.server.json +18 -0
- package/templates/go-wasm-app/vite.config.js +45 -0
- package/templates/ssr-template/package.json +16 -10
- package/templates/wasm/package.json +43 -43
- package/templates/wasm/src/App.tsx +2 -2
- package/dist/forms/index.d.ts +0 -2
- package/dist/forms/index.d.ts.map +0 -1
- package/dist/forms/types.d.ts +0 -2
- package/dist/forms/types.d.ts.map +0 -1
- package/dist/frontend/components/WasmLoader.d.ts +0 -2
- package/dist/frontend/components/WasmLoader.d.ts.map +0 -1
- package/dist/frontend/hooks/useWasm.d.ts +0 -2
- package/dist/frontend/hooks/useWasm.d.ts.map +0 -1
- package/dist/frontend/index.d.ts +0 -2
- package/dist/frontend/index.d.ts.map +0 -1
- package/dist/router/index.d.ts +0 -2
- package/dist/router/index.d.ts.map +0 -1
- package/dist/router/types.d.ts +0 -2
- package/dist/router/types.d.ts.map +0 -1
- package/dist/store/index.d.ts +0 -2
- package/dist/store/index.d.ts.map +0 -1
- package/dist/store/types.d.ts +0 -2
- package/dist/store/types.d.ts.map +0 -1
- package/templates/basic-app/package-lock.json +0 -7173
- package/templates/fullstack-template/package-lock.json +0 -3558
- package/templates/ssr-template/package-lock.json +0 -5380
- package/templates/wasm/package-lock.json +0 -5347
- package/templates/wasm/public/example.wasm +0 -0
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
import { render, useState, useEffect, useMemo, useRef, useErrorBoundary, createContext, useContext } from 'baraqex';
|
|
2
|
+
import './main.css';
|
|
3
|
+
// Create a theme context with proper typing
|
|
4
|
+
const ThemeContext = createContext('light');
|
|
5
|
+
// Main App Component - Fix the tab rendering logic
|
|
6
|
+
function App() {
|
|
7
|
+
const [activeTab, setActiveTab] = useState('counter');
|
|
8
|
+
const [theme, setTheme] = useState('light');
|
|
9
|
+
const [error, resetError] = useErrorBoundary();
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
document.title = `Baraqex Demo - ${activeTab.charAt(0).toUpperCase() + activeTab.slice(1)}`;
|
|
12
|
+
}, [activeTab]);
|
|
13
|
+
// Add debug logging for theme changes
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
console.log('Theme changed to:', theme);
|
|
16
|
+
}, [theme]);
|
|
17
|
+
// Add debug logging for tab changes
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
console.log('Active tab changed to:', activeTab);
|
|
20
|
+
}, [activeTab]);
|
|
21
|
+
if (error) {
|
|
22
|
+
return (<div className="min-h-screen bg-gradient-to-br from-red-50 to-red-100 flex items-center justify-center">
|
|
23
|
+
<div className="card max-w-md mx-auto text-center">
|
|
24
|
+
<div className="text-6xl mb-4">🚨</div>
|
|
25
|
+
<h1 className="text-2xl font-bold text-red-600 mb-4">Oops! Something went wrong</h1>
|
|
26
|
+
<p className="text-gray-600 mb-4">
|
|
27
|
+
{error.message || 'An unexpected error occurred'}
|
|
28
|
+
</p>
|
|
29
|
+
<button onClick={resetError} className="btn btn-primary">
|
|
30
|
+
Try Again
|
|
31
|
+
</button>
|
|
32
|
+
</div>
|
|
33
|
+
</div>);
|
|
34
|
+
}
|
|
35
|
+
// Function to render the current tab content
|
|
36
|
+
const renderTabContent = () => {
|
|
37
|
+
console.log('Rendering tab content for:', activeTab);
|
|
38
|
+
switch (activeTab) {
|
|
39
|
+
case 'counter':
|
|
40
|
+
console.log('Rendering Counter component');
|
|
41
|
+
return <Counter />;
|
|
42
|
+
case 'error':
|
|
43
|
+
console.log('Rendering BuggyComponent');
|
|
44
|
+
return <BuggyComponent />;
|
|
45
|
+
case 'context':
|
|
46
|
+
console.log('Rendering ContextConsumer component');
|
|
47
|
+
return <ContextConsumer />;
|
|
48
|
+
default:
|
|
49
|
+
console.log('Unknown tab, rendering Counter as fallback');
|
|
50
|
+
return <Counter />;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
return (<ThemeContext.Provider value={theme}>
|
|
54
|
+
<div className={`min-h-screen transition-colors ${theme === 'dark'
|
|
55
|
+
? 'bg-gradient-to-br from-gray-900 to-gray-800'
|
|
56
|
+
: 'bg-gradient-to-br from-blue-50 to-indigo-100'}`}>
|
|
57
|
+
<div className="container mx-auto px-4 py-8">
|
|
58
|
+
|
|
59
|
+
{/* Header with Logo */}
|
|
60
|
+
<header className="text-center mb-12">
|
|
61
|
+
<div className="flex items-center justify-center mb-6">
|
|
62
|
+
<img src="/logo.png" alt="Baraqex Logo" className="w-16 h-16 mr-4 rounded-lg shadow-lg" onError={(e) => {
|
|
63
|
+
// Fallback to emoji if logo doesn't exist
|
|
64
|
+
e.target.style.display = 'none';
|
|
65
|
+
e.target.nextElementSibling.style.display = 'block';
|
|
66
|
+
}}/>
|
|
67
|
+
<div className="text-6xl" style={{ display: 'none' }}>🚀</div>
|
|
68
|
+
<div>
|
|
69
|
+
<h1 className={`text-5xl font-bold mb-2 ${theme === 'dark' ? 'text-white' : 'text-gray-800'}`}>
|
|
70
|
+
Baraqex
|
|
71
|
+
</h1>
|
|
72
|
+
<p className={`text-xl ${theme === 'dark' ? 'text-gray-300' : 'text-gray-600'}`}>
|
|
73
|
+
Powered by Frontend Hamroun
|
|
74
|
+
</p>
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
{/* About Baraqex */}
|
|
79
|
+
<div className="card max-w-4xl mx-auto mb-8">
|
|
80
|
+
<h2 className="text-2xl font-bold text-gray-800 mb-6 text-center">
|
|
81
|
+
Welcome to Baraqex Framework! 🎉
|
|
82
|
+
</h2>
|
|
83
|
+
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 text-left">
|
|
84
|
+
<div>
|
|
85
|
+
<h3 className="text-lg font-semibold text-primary-600 mb-3">🚀 What is Baraqex?</h3>
|
|
86
|
+
<p className="text-gray-700 leading-relaxed mb-4">
|
|
87
|
+
Baraqex is a powerful, modern full-stack framework built on top of the Frontend Hamroun library.
|
|
88
|
+
It provides everything you need to build high-performance web applications with ease.
|
|
89
|
+
</p>
|
|
90
|
+
<p className="text-gray-700 leading-relaxed">
|
|
91
|
+
From simple SPAs to complex server-side rendered applications with WebAssembly integration,
|
|
92
|
+
Baraqex has you covered with a developer-friendly API and excellent performance.
|
|
93
|
+
</p>
|
|
94
|
+
</div>
|
|
95
|
+
<div>
|
|
96
|
+
<h3 className="text-lg font-semibold text-primary-600 mb-3">✨ Key Features</h3>
|
|
97
|
+
<ul className="space-y-2 text-gray-700">
|
|
98
|
+
<li className="flex items-center">
|
|
99
|
+
<span className="text-green-500 mr-2">✓</span>
|
|
100
|
+
Reactive state management with hooks
|
|
101
|
+
</li>
|
|
102
|
+
<li className="flex items-center">
|
|
103
|
+
<span className="text-green-500 mr-2">✓</span>
|
|
104
|
+
Server-side rendering (SSR) support
|
|
105
|
+
</li>
|
|
106
|
+
<li className="flex items-center">
|
|
107
|
+
<span className="text-green-500 mr-2">✓</span>
|
|
108
|
+
WebAssembly (WASM) integration
|
|
109
|
+
</li>
|
|
110
|
+
<li className="flex items-center">
|
|
111
|
+
<span className="text-green-500 mr-2">✓</span>
|
|
112
|
+
Built-in authentication & database support
|
|
113
|
+
</li>
|
|
114
|
+
<li className="flex items-center">
|
|
115
|
+
<span className="text-green-500 mr-2">✓</span>
|
|
116
|
+
TypeScript support out of the box
|
|
117
|
+
</li>
|
|
118
|
+
<li className="flex items-center">
|
|
119
|
+
<span className="text-green-500 mr-2">✓</span>
|
|
120
|
+
Production-ready with optimized builds
|
|
121
|
+
</li>
|
|
122
|
+
</ul>
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
|
|
126
|
+
<div className="mt-8 text-center">
|
|
127
|
+
<button onClick={() => {
|
|
128
|
+
const newTheme = theme === 'light' ? 'dark' : 'light';
|
|
129
|
+
console.log('Theme toggle clicked, changing from', theme, 'to', newTheme);
|
|
130
|
+
setTheme(newTheme);
|
|
131
|
+
}} className={`btn ${theme === 'dark' ? 'bg-yellow-400 text-gray-900 hover:bg-yellow-300' : 'bg-gray-800 text-white hover:bg-gray-700'}`}>
|
|
132
|
+
🌙 Toggle {theme === 'light' ? 'Dark' : 'Light'} Mode
|
|
133
|
+
</button>
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
</header>
|
|
137
|
+
|
|
138
|
+
{/* Tab Navigation */}
|
|
139
|
+
<div className="flex justify-center mb-8">
|
|
140
|
+
<div className="bg-white rounded-lg shadow-sm p-1 flex space-x-1 overflow-x-auto">
|
|
141
|
+
{[
|
|
142
|
+
{ id: 'counter', label: 'Counter', icon: '🔢' },
|
|
143
|
+
{ id: 'error', label: 'Error Test', icon: '🚨' },
|
|
144
|
+
{ id: 'context', label: 'Context', icon: '🔄' }
|
|
145
|
+
].map(tab => (<button key={tab.id} onClick={() => {
|
|
146
|
+
console.log('Tab clicked:', tab.id, 'current activeTab:', activeTab);
|
|
147
|
+
setActiveTab(tab.id);
|
|
148
|
+
}} className={`px-4 py-2 rounded-md transition-colors whitespace-nowrap ${activeTab === tab.id
|
|
149
|
+
? 'bg-primary-500 text-white'
|
|
150
|
+
: 'text-gray-600 hover:text-gray-800 hover:bg-gray-100'}`}>
|
|
151
|
+
<span className="mr-2">{tab.icon}</span>
|
|
152
|
+
{tab.label}
|
|
153
|
+
</button>))}
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
|
|
157
|
+
{/* Tab Content - Use the function to render content */}
|
|
158
|
+
<div className="flex justify-center mb-12">
|
|
159
|
+
{renderTabContent()}
|
|
160
|
+
</div>
|
|
161
|
+
|
|
162
|
+
{/* Footer */}
|
|
163
|
+
<footer className="text-center">
|
|
164
|
+
<div className="card max-w-4xl mx-auto">
|
|
165
|
+
<h3 className="text-xl font-semibold text-gray-800 mb-6">
|
|
166
|
+
🎯 Framework Capabilities Demo
|
|
167
|
+
</h3>
|
|
168
|
+
|
|
169
|
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
|
|
170
|
+
<div className="text-center">
|
|
171
|
+
<div className="text-3xl mb-2">⚡</div>
|
|
172
|
+
<h4 className="font-semibold text-gray-800 mb-2">State Management</h4>
|
|
173
|
+
<p className="text-sm text-gray-600">
|
|
174
|
+
useState, useEffect, useMemo, and useRef hooks working seamlessly
|
|
175
|
+
</p>
|
|
176
|
+
</div>
|
|
177
|
+
<div className="text-center">
|
|
178
|
+
<div className="text-3xl mb-2">🎨</div>
|
|
179
|
+
<h4 className="font-semibold text-gray-800 mb-2">Tailwind CSS</h4>
|
|
180
|
+
<p className="text-sm text-gray-600">
|
|
181
|
+
Utility-first CSS framework integrated for rapid UI development
|
|
182
|
+
</p>
|
|
183
|
+
</div>
|
|
184
|
+
<div className="text-center">
|
|
185
|
+
<div className="text-3xl mb-2">🛡️</div>
|
|
186
|
+
<h4 className="font-semibold text-gray-800 mb-2">Error Boundaries</h4>
|
|
187
|
+
<p className="text-sm text-gray-600">
|
|
188
|
+
Graceful error handling with useErrorBoundary hook
|
|
189
|
+
</p>
|
|
190
|
+
</div>
|
|
191
|
+
<div className="text-center">
|
|
192
|
+
<div className="text-3xl mb-2">🔄</div>
|
|
193
|
+
<h4 className="font-semibold text-gray-800 mb-2">Context API</h4>
|
|
194
|
+
<p className="text-sm text-gray-600">
|
|
195
|
+
Share state across components with createContext and useContext
|
|
196
|
+
</p>
|
|
197
|
+
</div>
|
|
198
|
+
</div>
|
|
199
|
+
|
|
200
|
+
<div className="border-t pt-6">
|
|
201
|
+
<h4 className="font-semibold text-gray-800 mb-4">🚀 Ready to build something amazing?</h4>
|
|
202
|
+
<p className="text-gray-600 mb-4">
|
|
203
|
+
Edit <code className="bg-gray-100 px-2 py-1 rounded text-sm">src/main.tsx</code> to customize this app,
|
|
204
|
+
or create new components to expand your application.
|
|
205
|
+
</p>
|
|
206
|
+
<div className="flex justify-center space-x-4">
|
|
207
|
+
<a href="https://www.baraqex.tech/docs" target="_blank" rel="noopener noreferrer" className="text-primary-600 hover:text-primary-800 font-medium">
|
|
208
|
+
📚 Documentation
|
|
209
|
+
</a>
|
|
210
|
+
<a href="https://github.com/mohamedx2/baraqex" target="_blank" rel="noopener noreferrer" className="text-primary-600 hover:text-primary-800 font-medium">
|
|
211
|
+
🐙 GitHub
|
|
212
|
+
</a>
|
|
213
|
+
<a href="https://www.baraqex.tech/examples" target="_blank" rel="noopener noreferrer" className="text-primary-600 hover:text-primary-800 font-medium">
|
|
214
|
+
🎯 Examples
|
|
215
|
+
</a>
|
|
216
|
+
</div>
|
|
217
|
+
</div>
|
|
218
|
+
</div>
|
|
219
|
+
</footer>
|
|
220
|
+
</div>
|
|
221
|
+
</div>
|
|
222
|
+
</ThemeContext.Provider>);
|
|
223
|
+
}
|
|
224
|
+
// Simple Counter Component with Tailwind styles - Fixed hooks
|
|
225
|
+
function Counter() {
|
|
226
|
+
// Initialize with proper values and ensure hooks work correctly
|
|
227
|
+
const [count, setCount] = useState(0);
|
|
228
|
+
const renderCount = useRef(0);
|
|
229
|
+
useEffect(() => {
|
|
230
|
+
renderCount.current = (renderCount.current || 0) + 1;
|
|
231
|
+
console.log('Counter effect running, count:', count, 'renders:', renderCount.current);
|
|
232
|
+
}, [count]);
|
|
233
|
+
// Ensure count is a number before calculation
|
|
234
|
+
const doubled = useMemo(() => {
|
|
235
|
+
const numCount = typeof count === 'number' ? count : 0;
|
|
236
|
+
console.log('Calculating doubled for count:', numCount);
|
|
237
|
+
return numCount * 2;
|
|
238
|
+
}, [count]);
|
|
239
|
+
// Add debug logging
|
|
240
|
+
console.log('Counter render - count:', count, 'doubled:', doubled, 'renders:', renderCount.current);
|
|
241
|
+
return (<div className="card max-w-sm mx-auto">
|
|
242
|
+
<h2 className="text-2xl font-bold text-gray-800 mb-4 text-center">
|
|
243
|
+
Counter Example
|
|
244
|
+
</h2>
|
|
245
|
+
<div className="text-center">
|
|
246
|
+
<div className="text-4xl font-bold text-primary-600 mb-6">
|
|
247
|
+
{typeof count === 'number' ? count : 0}
|
|
248
|
+
</div>
|
|
249
|
+
<div className="mb-4">
|
|
250
|
+
<p className="text-sm text-gray-600 mb-2">
|
|
251
|
+
Doubled: {typeof doubled === 'number' ? doubled : 'Error'}
|
|
252
|
+
</p>
|
|
253
|
+
<p className="text-sm text-gray-600">
|
|
254
|
+
Renders: {renderCount.current || 0}
|
|
255
|
+
</p>
|
|
256
|
+
</div>
|
|
257
|
+
<div className="space-x-2">
|
|
258
|
+
<button onClick={() => {
|
|
259
|
+
console.log('Increment clicked, current count:', count);
|
|
260
|
+
const newCount = (typeof count === 'number' ? count : 0) + 1;
|
|
261
|
+
console.log('Setting new count:', newCount);
|
|
262
|
+
setCount(newCount);
|
|
263
|
+
}} className="btn btn-primary">
|
|
264
|
+
Increment
|
|
265
|
+
</button>
|
|
266
|
+
<button onClick={() => {
|
|
267
|
+
console.log('Decrement clicked, current count:', count);
|
|
268
|
+
const newCount = (typeof count === 'number' ? count : 0) - 1;
|
|
269
|
+
console.log('Setting new count:', newCount);
|
|
270
|
+
setCount(newCount);
|
|
271
|
+
}} className="btn btn-secondary">
|
|
272
|
+
Decrement
|
|
273
|
+
</button>
|
|
274
|
+
<button onClick={() => {
|
|
275
|
+
console.log('Reset clicked');
|
|
276
|
+
setCount(0);
|
|
277
|
+
}} className="btn btn-danger">
|
|
278
|
+
Reset
|
|
279
|
+
</button>
|
|
280
|
+
</div>
|
|
281
|
+
</div>
|
|
282
|
+
</div>);
|
|
283
|
+
}
|
|
284
|
+
// Error boundary test component with debugging
|
|
285
|
+
function BuggyComponent() {
|
|
286
|
+
console.log('BuggyComponent rendering...');
|
|
287
|
+
const [shouldError, setShouldError] = useState(false);
|
|
288
|
+
if (shouldError) {
|
|
289
|
+
throw new Error("Test error from BuggyComponent");
|
|
290
|
+
}
|
|
291
|
+
return (<div className="card max-w-sm mx-auto">
|
|
292
|
+
<h3 className="text-lg font-semibold text-gray-800 mb-4 text-center">
|
|
293
|
+
Error Boundary Test
|
|
294
|
+
</h3>
|
|
295
|
+
<p className="text-sm text-gray-600 mb-4 text-center">
|
|
296
|
+
Click the button below to test error handling
|
|
297
|
+
</p>
|
|
298
|
+
<div className="text-center">
|
|
299
|
+
<button onClick={() => {
|
|
300
|
+
console.log('Triggering error...');
|
|
301
|
+
setShouldError(true);
|
|
302
|
+
}} className="btn btn-danger">
|
|
303
|
+
Trigger Error
|
|
304
|
+
</button>
|
|
305
|
+
</div>
|
|
306
|
+
</div>);
|
|
307
|
+
}
|
|
308
|
+
// Context test component - fixed to properly extract theme value
|
|
309
|
+
function ContextConsumer() {
|
|
310
|
+
const theme = useContext(ThemeContext);
|
|
311
|
+
// Extract the actual theme value - handle both direct values and wrapped objects
|
|
312
|
+
const actualTheme = typeof theme === 'object' && theme !== null
|
|
313
|
+
? theme.value || 'light'
|
|
314
|
+
: (typeof theme === 'string' ? theme : 'light');
|
|
315
|
+
console.log('Context consumer - raw theme:', theme, 'actual theme:', actualTheme);
|
|
316
|
+
return (<div className="card max-w-sm mx-auto">
|
|
317
|
+
<h3 className="text-lg font-semibold text-gray-800 mb-4 text-center">
|
|
318
|
+
Context API Test
|
|
319
|
+
</h3>
|
|
320
|
+
<div className={`p-4 rounded-md border text-center ${actualTheme === 'dark' ? 'border-gray-600 bg-gray-700 text-white' : 'border-gray-300 bg-gray-50'}`}>
|
|
321
|
+
<p className="text-sm mb-2">Current theme: <strong>{actualTheme}</strong></p>
|
|
322
|
+
<p className="text-xs text-gray-500">
|
|
323
|
+
Theme is provided by Context API
|
|
324
|
+
</p>
|
|
325
|
+
</div>
|
|
326
|
+
</div>);
|
|
327
|
+
}
|
|
328
|
+
// Mount the app with null check and debug logging
|
|
329
|
+
const rootElement = document.getElementById('root');
|
|
330
|
+
if (rootElement) {
|
|
331
|
+
console.log('Mounting app to root element');
|
|
332
|
+
render(<App />, rootElement);
|
|
333
|
+
}
|
|
334
|
+
else {
|
|
335
|
+
console.error('Root element not found');
|
|
336
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { hydrate, jsx } from 'frontend-hamroun';
|
|
2
|
+
|
|
3
|
+
// Create a sample virtual DOM matching what was rendered on the server
|
|
4
|
+
const app = {
|
|
5
|
+
type: 'div',
|
|
6
|
+
props: {
|
|
7
|
+
id: 'app',
|
|
8
|
+
children: [
|
|
9
|
+
{
|
|
10
|
+
type: 'h1',
|
|
11
|
+
props: {
|
|
12
|
+
children: 'Hello from Server-Side Rendering!'
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
type: 'p',
|
|
17
|
+
props: {
|
|
18
|
+
children: `This page was rendered at ${new Date().toISOString()}`
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
type: 'button',
|
|
23
|
+
props: {
|
|
24
|
+
id: 'counter-btn',
|
|
25
|
+
className: 'btn',
|
|
26
|
+
onClick: () => {
|
|
27
|
+
let count = 1; // Start at 1 since we're updating
|
|
28
|
+
const btn = document.getElementById('counter-btn');
|
|
29
|
+
if (btn) {
|
|
30
|
+
btn.textContent = `Click me (${count})`;
|
|
31
|
+
btn.addEventListener('click', () => {
|
|
32
|
+
count++;
|
|
33
|
+
btn.textContent = `Click me (${count})`;
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
children: 'Click me (0)'
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// Wait for DOM to be fully loaded before hydrating
|
|
45
|
+
window.addEventListener('DOMContentLoaded', () => {
|
|
46
|
+
console.log('Hydrating client-side content...');
|
|
47
|
+
|
|
48
|
+
// Get the container element
|
|
49
|
+
const container = document.getElementById('app');
|
|
50
|
+
if (!container) {
|
|
51
|
+
console.error('Could not find app container for hydration');
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Hydrate the app
|
|
56
|
+
hydrate(app, container);
|
|
57
|
+
console.log('Hydration complete!');
|
|
58
|
+
});
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple JSX implementation for server-side rendering
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Render a virtual DOM node to HTML string
|
|
6
|
+
export function renderToString(vnode) {
|
|
7
|
+
// Handle null or undefined
|
|
8
|
+
if (vnode == null) return '';
|
|
9
|
+
|
|
10
|
+
// Handle primitive values
|
|
11
|
+
if (typeof vnode === 'string' || typeof vnode === 'number')
|
|
12
|
+
return escapeHtml(String(vnode));
|
|
13
|
+
|
|
14
|
+
// Handle arrays (like fragments)
|
|
15
|
+
if (Array.isArray(vnode))
|
|
16
|
+
return vnode.map(renderToString).join('');
|
|
17
|
+
|
|
18
|
+
// Handle functional components
|
|
19
|
+
if (typeof vnode.type === 'function') {
|
|
20
|
+
try {
|
|
21
|
+
const result = vnode.type(vnode.props || {});
|
|
22
|
+
return renderToString(result);
|
|
23
|
+
} catch (err) {
|
|
24
|
+
console.error('Error rendering component:', err);
|
|
25
|
+
return `<div class="error">Error: ${escapeHtml(err.message)}</div>`;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Handle Fragment
|
|
30
|
+
if (vnode.type === Symbol.for('react.fragment')) {
|
|
31
|
+
return renderToString(vnode.props.children);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Handle regular DOM elements
|
|
35
|
+
if (typeof vnode.type === 'string') {
|
|
36
|
+
let props = vnode.props || {};
|
|
37
|
+
let children = props.children || [];
|
|
38
|
+
if (!Array.isArray(children)) {
|
|
39
|
+
children = [children];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Build opening tag with attributes
|
|
43
|
+
let html = `<${vnode.type}`;
|
|
44
|
+
|
|
45
|
+
for (const [key, value] of Object.entries(props)) {
|
|
46
|
+
if (key === 'children' || key === 'dangerouslySetInnerHTML') continue;
|
|
47
|
+
|
|
48
|
+
// Handle event handlers (they should be ignored in SSR)
|
|
49
|
+
if (key.startsWith('on')) continue;
|
|
50
|
+
|
|
51
|
+
// Handle className -> class
|
|
52
|
+
if (key === 'className') {
|
|
53
|
+
html += ` class="${escapeHtml(value)}"`;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Handle style objects
|
|
58
|
+
if (key === 'style' && typeof value === 'object') {
|
|
59
|
+
const styleStr = Object.entries(value)
|
|
60
|
+
.map(([k, v]) => `${kebabCase(k)}:${v}`)
|
|
61
|
+
.join(';');
|
|
62
|
+
html += ` style="${escapeHtml(styleStr)}"`;
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Handle boolean attributes
|
|
67
|
+
if (value === true) {
|
|
68
|
+
html += ` ${key}`;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Skip false boolean attributes
|
|
73
|
+
if (value === false) continue;
|
|
74
|
+
|
|
75
|
+
// Regular attributes
|
|
76
|
+
if (value != null) {
|
|
77
|
+
html += ` ${key}="${escapeHtml(value)}"`;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Handle self-closing tags
|
|
82
|
+
const selfClosing = ['img', 'input', 'br', 'hr', 'meta', 'link', 'area', 'base', 'col', 'embed', 'param', 'source', 'track', 'wbr'];
|
|
83
|
+
if (selfClosing.includes(vnode.type)) {
|
|
84
|
+
return `${html} />`;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Add closing bracket for opening tag
|
|
88
|
+
html += '>';
|
|
89
|
+
|
|
90
|
+
// Handle dangerouslySetInnerHTML
|
|
91
|
+
if (props.dangerouslySetInnerHTML) {
|
|
92
|
+
html += props.dangerouslySetInnerHTML.__html || '';
|
|
93
|
+
} else {
|
|
94
|
+
// Add children
|
|
95
|
+
for (const child of children) {
|
|
96
|
+
html += renderToString(child);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Add closing tag
|
|
101
|
+
html += `</${vnode.type}>`;
|
|
102
|
+
|
|
103
|
+
return html;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Unknown node type
|
|
107
|
+
console.warn('Unknown vnode type:', vnode.type);
|
|
108
|
+
return '';
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Create a JSX element
|
|
112
|
+
export function jsx(type, props, ...children) {
|
|
113
|
+
props = props || {};
|
|
114
|
+
if (children.length) {
|
|
115
|
+
props.children = children.length === 1 ? children[0] : children;
|
|
116
|
+
}
|
|
117
|
+
return { type, props };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Helper for escaping HTML
|
|
121
|
+
function escapeHtml(str) {
|
|
122
|
+
return String(str)
|
|
123
|
+
.replace(/&/g, '&')
|
|
124
|
+
.replace(/</g, '<')
|
|
125
|
+
.replace(/>/g, '>')
|
|
126
|
+
.replace(/"/g, '"')
|
|
127
|
+
.replace(/'/g, ''');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Helper to convert camelCase to kebab-case for CSS properties
|
|
131
|
+
function kebabCase(str) {
|
|
132
|
+
return str.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Create a basic requestLogger middleware
|
|
136
|
+
export function requestLogger(req, res, next) {
|
|
137
|
+
console.log(`${new Date().toISOString()} - ${req.method} ${req.url}`);
|
|
138
|
+
next();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Create a basic error handler middleware
|
|
142
|
+
export function errorHandler(err, req, res, next) {
|
|
143
|
+
console.error('Server error:', err);
|
|
144
|
+
res.status(500).send('Server Error');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Create a basic 404 handler middleware
|
|
148
|
+
export function notFoundHandler(req, res) {
|
|
149
|
+
res.status(404).send('Not Found');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Create a basic rate limiter middleware
|
|
153
|
+
export function rateLimit(options = { windowMs: 60000, max: 100 }) {
|
|
154
|
+
const requests = new Map();
|
|
155
|
+
|
|
156
|
+
return (req, res, next) => {
|
|
157
|
+
const ip = req.ip || 'unknown';
|
|
158
|
+
const now = Date.now();
|
|
159
|
+
|
|
160
|
+
if (!requests.has(ip)) {
|
|
161
|
+
requests.set(ip, []);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const reqs = requests.get(ip);
|
|
165
|
+
const validReqs = reqs.filter(time => now - time < options.windowMs);
|
|
166
|
+
|
|
167
|
+
validReqs.push(now);
|
|
168
|
+
requests.set(ip, validReqs);
|
|
169
|
+
|
|
170
|
+
if (validReqs.length > options.max) {
|
|
171
|
+
return res.status(429).send('Too Many Requests');
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
next();
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Export other necessary functions
|
|
179
|
+
export const Fragment = Symbol.for('react.fragment');
|
|
180
|
+
export function loadGoWasmFromFile() {
|
|
181
|
+
throw new Error('WASM not supported in this environment');
|
|
182
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ssr-app",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "node server.js",
|
|
7
|
+
"start": "node server.js",
|
|
8
|
+
"build": "echo 'No build step required for this template'"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"compression": "^1.8.0",
|
|
12
|
+
"cors": "^2.8.5",
|
|
13
|
+
"dotenv": "^16.0.3",
|
|
14
|
+
"express": "^4.18.2",
|
|
15
|
+
"frontend-hamroun": "^1.2.79",
|
|
16
|
+
"node-fetch": "^3.3.1"
|
|
17
|
+
}
|
|
18
|
+
}
|