frontend-hamroun 1.2.79 → 1.2.80
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/LICENSE +21 -0
- package/README.md +129 -1513
- package/bin/cli.js +505 -144
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.client.cjs +2 -0
- package/dist/index.client.cjs.map +1 -0
- package/dist/index.client.js +26 -0
- package/dist/index.client.js.map +1 -0
- package/dist/index.js +298 -1
- package/dist/index.js.map +1 -0
- package/dist/jsx-runtime.cjs +2 -0
- package/dist/jsx-runtime.cjs.map +1 -0
- package/dist/jsx-runtime.js +93 -1
- package/dist/jsx-runtime.js.map +1 -0
- package/dist/renderer-Bo9zkUZ_.js +52 -0
- package/dist/renderer-Bo9zkUZ_.js.map +1 -0
- package/dist/renderer-Din1y3YM.cjs +2 -0
- package/dist/renderer-Din1y3YM.cjs.map +1 -0
- package/dist/server-renderer-CqIpQ-od.cjs +2 -0
- package/dist/server-renderer-CqIpQ-od.cjs.map +1 -0
- package/dist/server-renderer-QHt45Ip2.js +255 -0
- package/dist/server-renderer-QHt45Ip2.js.map +1 -0
- package/dist/server-renderer.cjs +2 -0
- package/dist/server-renderer.cjs.map +1 -0
- package/dist/server-renderer.js +5 -1
- package/dist/server-renderer.js.map +1 -0
- package/package.json +77 -120
- package/templates/basic-app/build.js +22 -0
- package/templates/basic-app/bun.lock +196 -0
- package/templates/basic-app/dev.js +27 -0
- package/templates/basic-app/docs/rapport_pfe.aux +27 -27
- package/templates/basic-app/docs/rapport_pfe.out +10 -10
- package/templates/basic-app/docs/rapport_pfe.toc +14 -14
- package/templates/basic-app/esbuild.config.js +28 -0
- package/templates/basic-app/index.html +1 -1
- package/templates/{fullstack-app → basic-app}/package-lock.json +4185 -5094
- package/templates/basic-app/package.json +29 -28
- package/templates/basic-app/server.js +24 -0
- package/templates/basic-app/src/App.tsx +26 -0
- package/templates/basic-app/src/client.tsx +11 -0
- package/templates/basic-app/src/components/Counter.tsx +18 -0
- package/templates/basic-app/src/jsx-shim.ts +4 -0
- package/templates/basic-app/src/main.tsx +0 -1
- package/templates/basic-app/src/server.ts +52 -0
- package/templates/basic-app/tsconfig.server.json +11 -0
- package/templates/fullstack-app/build/main.css +874 -874
- package/templates/fullstack-app/build/main.css.map +7 -7
- package/templates/fullstack-app/build/main.js +967 -967
- package/templates/fullstack-app/build/main.js.map +7 -7
- package/templates/fullstack-app/public/styles.css +768 -768
- package/templates/go/example.go +99 -154
- package/templates/go-wasm-app/babel.config.js +2 -8
- package/templates/go-wasm-app/package.json +12 -21
- package/templates/go-wasm-app/public/wasm/wasm_exec.js +561 -561
- package/templates/go-wasm-app/public/wasm/wasm_exec_node.js +39 -39
- package/templates/go-wasm-app/server.js +510 -59
- package/templates/go-wasm-app/src/app.js +2 -22
- package/templates/go-wasm-app/src/wasm/example.go +75 -75
- package/templates/go-wasm-app/vite.config.js +5 -16
- package/templates/ssr-template/server.js +2 -2
- package/templates/ssr-template/vite.config.js +5 -16
- package/dist/Counter.d.ts +0 -0
- package/dist/batch/package.json +0 -16
- package/dist/client-router/package.json +0 -16
- package/dist/component/package.json +0 -16
- package/dist/context/package.json +0 -16
- package/dist/event-bus/package.json +0 -16
- package/dist/forms/package.json +0 -16
- package/dist/hooks/package.json +0 -16
- package/dist/hooks-0728361a.cjs +0 -1
- package/dist/hooks-b58f947c.js +0 -133
- package/dist/hooks.js +0 -1
- package/dist/hooks.mjs +0 -13
- package/dist/index.mjs +0 -137
- package/dist/jsx-runtime/package.json +0 -16
- package/dist/jsx-runtime.mjs +0 -64
- package/dist/lifecycle-events/package.json +0 -16
- package/dist/package.json +0 -71
- package/dist/render-component/package.json +0 -16
- package/dist/renderer/package.json +0 -16
- package/dist/renderer.js +0 -1
- package/dist/renderer.mjs +0 -27
- package/dist/router/package.json +0 -16
- package/dist/server/package.json +0 -17
- package/dist/server/src/batch.d.ts +0 -3
- package/dist/server/src/batch.js +0 -23
- package/dist/server/src/batch.js.map +0 -1
- package/dist/server/src/client-router.d.ts +0 -60
- package/dist/server/src/client-router.js +0 -210
- package/dist/server/src/client-router.js.map +0 -1
- package/dist/server/src/component.d.ts +0 -14
- package/dist/server/src/component.js +0 -106
- package/dist/server/src/component.js.map +0 -1
- package/dist/server/src/context.d.ts +0 -13
- package/dist/server/src/context.js +0 -21
- package/dist/server/src/context.js.map +0 -1
- package/dist/server/src/event-bus.d.ts +0 -23
- package/dist/server/src/event-bus.js +0 -75
- package/dist/server/src/event-bus.js.map +0 -1
- package/dist/server/src/forms.d.ts +0 -40
- package/dist/server/src/forms.js +0 -148
- package/dist/server/src/forms.js.map +0 -1
- package/dist/server/src/hooks.d.ts +0 -12
- package/dist/server/src/hooks.js +0 -170
- package/dist/server/src/hooks.js.map +0 -1
- package/dist/server/src/index.client.d.ts +0 -12
- package/dist/server/src/index.client.js +0 -14
- package/dist/server/src/index.client.js.map +0 -1
- package/dist/server/src/index.d.ts +0 -88
- package/dist/server/src/index.js +0 -79
- package/dist/server/src/index.js.map +0 -1
- package/dist/server/src/jsx-runtime/jsx-dev-runtime.d.ts +0 -1
- package/dist/server/src/jsx-runtime/jsx-dev-runtime.js +0 -2
- package/dist/server/src/jsx-runtime/jsx-dev-runtime.js.map +0 -1
- package/dist/server/src/jsx-runtime/jsx-runtime.d.ts +0 -4
- package/dist/server/src/jsx-runtime/jsx-runtime.js +0 -41
- package/dist/server/src/jsx-runtime/jsx-runtime.js.map +0 -1
- package/dist/server/src/jsx-runtime.d.ts +0 -20
- package/dist/server/src/jsx-runtime.js +0 -105
- package/dist/server/src/jsx-runtime.js.map +0 -1
- package/dist/server/src/lifecycle-events.d.ts +0 -108
- package/dist/server/src/lifecycle-events.js +0 -177
- package/dist/server/src/lifecycle-events.js.map +0 -1
- package/dist/server/src/renderComponent.d.ts +0 -13
- package/dist/server/src/renderComponent.js +0 -30
- package/dist/server/src/renderComponent.js.map +0 -1
- package/dist/server/src/renderer.d.ts +0 -2
- package/dist/server/src/renderer.js +0 -31
- package/dist/server/src/renderer.js.map +0 -1
- package/dist/server/src/router.d.ts +0 -55
- package/dist/server/src/router.js +0 -166
- package/dist/server/src/router.js.map +0 -1
- package/dist/server/src/server/api-router.d.ts +0 -15
- package/dist/server/src/server/api-router.js +0 -111
- package/dist/server/src/server/api-router.js.map +0 -1
- package/dist/server/src/server/auth.d.ts +0 -32
- package/dist/server/src/server/auth.js +0 -80
- package/dist/server/src/server/auth.js.map +0 -1
- package/dist/server/src/server/database.d.ts +0 -24
- package/dist/server/src/server/database.js +0 -135
- package/dist/server/src/server/database.js.map +0 -1
- package/dist/server/src/server/index.d.ts +0 -116
- package/dist/server/src/server/index.js +0 -508
- package/dist/server/src/server/index.js.map +0 -1
- package/dist/server/src/server/middleware.d.ts +0 -11
- package/dist/server/src/server/middleware.js +0 -46
- package/dist/server/src/server/middleware.js.map +0 -1
- package/dist/server/src/server/server.d.ts +0 -9
- package/dist/server/src/server/server.js +0 -87
- package/dist/server/src/server/server.js.map +0 -1
- package/dist/server/src/server/templates.d.ts +0 -30
- package/dist/server/src/server/templates.js +0 -208
- package/dist/server/src/server/templates.js.map +0 -1
- package/dist/server/src/server/types.d.ts +0 -38
- package/dist/server/src/server/types.js +0 -4
- package/dist/server/src/server/types.js.map +0 -1
- package/dist/server/src/server/utils.d.ts +0 -70
- package/dist/server/src/server/utils.js +0 -156
- package/dist/server/src/server/utils.js.map +0 -1
- package/dist/server/src/server/wasm.d.ts +0 -9
- package/dist/server/src/server/wasm.js +0 -117
- package/dist/server/src/server/wasm.js.map +0 -1
- package/dist/server/src/server-renderer.d.ts +0 -5
- package/dist/server/src/server-renderer.js +0 -106
- package/dist/server/src/server-renderer.js.map +0 -1
- package/dist/server/src/server-types.d.ts +0 -42
- package/dist/server/src/server-types.js +0 -6
- package/dist/server/src/server-types.js.map +0 -1
- package/dist/server/src/store.d.ts +0 -41
- package/dist/server/src/store.js +0 -99
- package/dist/server/src/store.js.map +0 -1
- package/dist/server/src/types.d.ts +0 -19
- package/dist/server/src/types.js +0 -2
- package/dist/server/src/types.js.map +0 -1
- package/dist/server/src/utils.d.ts +0 -46
- package/dist/server/src/utils.js +0 -144
- package/dist/server/src/utils.js.map +0 -1
- package/dist/server/src/vdom.d.ts +0 -8
- package/dist/server/src/vdom.js +0 -22
- package/dist/server/src/vdom.js.map +0 -1
- package/dist/server/src/wasm.d.ts +0 -36
- package/dist/server/src/wasm.js +0 -159
- package/dist/server/src/wasm.js.map +0 -1
- package/dist/server/tsconfig.server.tsbuildinfo +0 -1
- package/dist/server-renderer/package.json +0 -16
- package/dist/server-renderer.mjs +0 -64
- package/dist/store/package.json +0 -16
- package/dist/types/package.json +0 -16
- package/dist/utils/package.json +0 -16
- package/dist/vdom/package.json +0 -16
- package/dist/wasm/package.json +0 -16
- package/dist/wasm.js +0 -1
- package/dist/wasm.mjs +0 -103
- package/templates/basic-app/docs/rapport_pfe.log +0 -399
- package/templates/complete-app/client.js +0 -58
- package/templates/complete-app/package-lock.json +0 -2536
- package/templates/complete-app/package.json +0 -17
- package/templates/complete-app/pages/about.js +0 -119
- package/templates/complete-app/pages/index.js +0 -157
- package/templates/complete-app/pages/wasm-demo.js +0 -290
- package/templates/complete-app/public/client.js +0 -80
- package/templates/complete-app/public/index.html +0 -47
- package/templates/complete-app/public/styles.css +0 -579
- package/templates/complete-app/readme.md +0 -188
- package/templates/complete-app/server.js +0 -417
- package/templates/complete-app/server.ts +0 -275
- package/templates/complete-app/src/App.tsx +0 -59
- package/templates/complete-app/src/client.ts +0 -61
- package/templates/complete-app/src/client.tsx +0 -18
- package/templates/complete-app/src/pages/index.tsx +0 -51
- package/templates/complete-app/src/server.ts +0 -218
- package/templates/complete-app/tsconfig.json +0 -22
- package/templates/complete-app/tsconfig.server.json +0 -19
- package/templates/complete-app/vite.config.js +0 -57
- package/templates/complete-app/vite.config.ts +0 -30
- package/templates/go-wasm-app/build.config.js +0 -62
- package/templates/go-wasm-app/build.js +0 -218
- package/templates/go-wasm-app/package-lock.json +0 -3732
package/README.md
CHANGED
@@ -1,1600 +1,216 @@
|
|
1
|
-
# Frontend Hamroun
|
1
|
+
# Frontend Hamroun Framework
|
2
2
|
|
3
|
-
|
4
|
-
<img src="https://drive.google.com/uc?export=view&id=15VsMSNDhWAfV_R6ZWJltOJd-RMs9UH_y" alt="Frontend Hamroun Logo" width="300">
|
5
|
-
</p>
|
3
|
+
A lightweight, high-performance React-like framework with built-in TypeScript support and server-side rendering capabilities.
|
6
4
|
|
7
|
-
|
5
|
+
## 🚀 Features
|
8
6
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
- [Introduction](#-introduction)
|
17
|
-
- [Installation](#-installation)
|
18
|
-
- [Quick Start](#-quick-start)
|
19
|
-
- [Core Concepts](#-core-concepts)
|
20
|
-
- [Frontend Features](#-frontend-features)
|
21
|
-
- [Backend Features](#-backend-features)
|
22
|
-
- [WebAssembly Integration](#-webassembly-integration)
|
23
|
-
- [API Reference](#-api-reference)
|
24
|
-
- [CLI Tools](#-cli-tools)
|
25
|
-
- [Advanced Usage](#-advanced-usage)
|
26
|
-
- [TypeScript Support](#-typescript-support)
|
27
|
-
- [Browser Compatibility](#-browser-compatibility)
|
28
|
-
- [FAQ](#-frequently-asked-questions)
|
29
|
-
- [License](#-license)
|
30
|
-
|
31
|
-
## 🚀 Introduction
|
32
|
-
|
33
|
-
Frontend Hamroun is a lightweight (~5KB gzipped) JavaScript framework designed for building modern web applications. It combines React-like frontend development with powerful backend capabilities in a single, unified package.
|
34
|
-
|
35
|
-
### Key Features
|
36
|
-
|
37
|
-
- **Efficient Virtual DOM**: Minimizes DOM operations with intelligent diffing
|
38
|
-
- **Complete Hooks API**: useState, useEffect, useRef, useMemo, and more
|
39
|
-
- **Full-Stack Solution**: Unified frontend and backend development
|
40
|
-
- **Server-Side Rendering**: Optimized SSR with hydration
|
41
|
-
- **WebAssembly Integration**: Built-in Go WASM support for high-performance computing
|
42
|
-
- **File-Based Routing**: Intuitive API endpoint creation
|
43
|
-
- **Database Integration**: Built-in support for MongoDB, MySQL, PostgreSQL
|
44
|
-
- **TypeScript Support**: Full type definitions and excellent DX
|
45
|
-
- **Automatic Batch Updates**: Efficient state management
|
46
|
-
- **CLI Tooling**: Scaffolding for components, pages, and API routes
|
7
|
+
- **React-like API** - Familiar hooks and component patterns
|
8
|
+
- **TypeScript First** - Complete type safety out of the box
|
9
|
+
- **Server-Side Rendering** - Built-in SSR for SEO and performance
|
10
|
+
- **Lightweight** - ~8KB gzipped runtime
|
11
|
+
- **Fast Virtual DOM** - Efficient O(n) diffing algorithm
|
12
|
+
- **Modern Tooling** - Vite-powered development experience
|
13
|
+
- **WebAssembly Ready** - Optional WASM integration for performance
|
47
14
|
|
48
15
|
## 📦 Installation
|
49
16
|
|
50
17
|
```bash
|
51
|
-
#
|
52
|
-
npm install frontend-hamroun
|
18
|
+
# Install CLI globally
|
19
|
+
npm install -g frontend-hamroun
|
53
20
|
|
54
|
-
#
|
55
|
-
|
21
|
+
# Create new project
|
22
|
+
frontend-hamroun create my-app
|
23
|
+
cd my-app
|
56
24
|
|
57
|
-
#
|
58
|
-
|
25
|
+
# Start development
|
26
|
+
npm run dev
|
59
27
|
```
|
60
28
|
|
61
|
-
##
|
29
|
+
## 🏗️ Quick Start
|
62
30
|
|
63
|
-
|
64
|
-
<summary><b>Method 1: Create a new project with CLI</b></summary>
|
31
|
+
### 1. Create Components
|
65
32
|
|
66
33
|
```bash
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
# Or with the frontend-hamroun CLI
|
71
|
-
npx frontend-hamroun create my-app
|
34
|
+
frontend-hamroun add:component Button
|
35
|
+
frontend-hamroun add:component Header
|
72
36
|
```
|
73
37
|
|
74
|
-
|
38
|
+
### 2. Add Pages
|
75
39
|
|
76
40
|
```bash
|
77
|
-
|
78
|
-
|
79
|
-
npm run dev
|
41
|
+
frontend-hamroun add:page home
|
42
|
+
frontend-hamroun add:page about
|
80
43
|
```
|
81
44
|
|
82
|
-
|
83
|
-
</details>
|
84
|
-
|
85
|
-
<details>
|
86
|
-
<summary><b>Method 2: Add to an existing project</b></summary>
|
45
|
+
### 3. Create API Endpoints
|
87
46
|
|
88
47
|
```bash
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
# Import and use in your code
|
93
|
-
import { render, useState } from 'frontend-hamroun';
|
48
|
+
frontend-hamroun add:api users
|
49
|
+
frontend-hamroun add:api posts
|
94
50
|
```
|
95
51
|
|
96
|
-
|
97
|
-
// Create a simple app
|
98
|
-
import { render, useState } from 'frontend-hamroun';
|
99
|
-
|
100
|
-
function Counter() {
|
101
|
-
const [count, setCount] = useState(0);
|
102
|
-
|
103
|
-
return (
|
104
|
-
<div>
|
105
|
-
<h1>Count: {count}</h1>
|
106
|
-
<button onClick={() => setCount(count + 1)}>
|
107
|
-
Increment
|
108
|
-
</button>
|
109
|
-
</div>
|
110
|
-
);
|
111
|
-
}
|
112
|
-
|
113
|
-
render(<Counter />, document.getElementById('root'));
|
114
|
-
```
|
115
|
-
</details>
|
116
|
-
|
117
|
-
## 🧠 Core Concepts
|
118
|
-
|
119
|
-
### Virtual DOM
|
120
|
-
|
121
|
-
Frontend Hamroun uses a lightweight Virtual DOM implementation to efficiently update the real DOM. It only applies the minimal necessary changes by:
|
52
|
+
### 4. Build Your App
|
122
53
|
|
123
54
|
```jsx
|
124
|
-
|
55
|
+
import { render, useState, useEffect } from 'frontend-hamroun';
|
56
|
+
|
125
57
|
function App() {
|
126
58
|
const [count, setCount] = useState(0);
|
127
|
-
return (
|
128
|
-
<div>
|
129
|
-
<h1>Counter</h1>
|
130
|
-
<p>Count: {count}</p> {/* Only this text node updates */}
|
131
|
-
<button onClick={() => setCount(count + 1)}>Increment</button>
|
132
|
-
</div>
|
133
|
-
);
|
134
|
-
}
|
135
|
-
```
|
136
|
-
|
137
|
-
### Component Model
|
138
|
-
|
139
|
-
Components are the building blocks of your UI. Each component encapsulates its own logic and rendering:
|
140
|
-
|
141
|
-
```jsx
|
142
|
-
// Function components with hooks
|
143
|
-
function Greeting({ name }) {
|
144
|
-
// State management
|
145
|
-
const [clicked, setClicked] = useState(false);
|
146
59
|
|
147
|
-
// Side effects
|
148
60
|
useEffect(() => {
|
149
|
-
document.title = `
|
150
|
-
|
151
|
-
}, [name]);
|
61
|
+
document.title = `Count: ${count}`;
|
62
|
+
}, [count]);
|
152
63
|
|
153
64
|
return (
|
154
65
|
<div>
|
155
|
-
<h1>
|
156
|
-
<
|
157
|
-
|
66
|
+
<h1>Frontend Hamroun App</h1>
|
67
|
+
<p>Count: {count}</p>
|
68
|
+
<button onClick={() => setCount(count + 1)}>
|
69
|
+
Increment
|
158
70
|
</button>
|
159
71
|
</div>
|
160
72
|
);
|
161
73
|
}
|
162
|
-
```
|
163
|
-
|
164
|
-
### WebAssembly Integration
|
165
74
|
|
166
|
-
|
167
|
-
|
168
|
-
```jsx
|
169
|
-
import { useEffect, useState, loadGoWasm } from 'frontend-hamroun';
|
170
|
-
|
171
|
-
function GoCalculator() {
|
172
|
-
const [result, setResult] = useState(0);
|
173
|
-
const [wasm, setWasm] = useState(null);
|
174
|
-
|
175
|
-
// Load the Go WASM module
|
176
|
-
useEffect(() => {
|
177
|
-
async function loadModule() {
|
178
|
-
const instance = await loadGoWasm('/calculator.wasm');
|
179
|
-
setWasm(instance);
|
180
|
-
}
|
181
|
-
loadModule();
|
182
|
-
}, []);
|
183
|
-
|
184
|
-
// Call Go functions from JavaScript
|
185
|
-
const calculate = () => {
|
186
|
-
if (wasm) {
|
187
|
-
// Call the Add function defined in Go
|
188
|
-
const sum = wasm.functions.goAdd(5, 7);
|
189
|
-
setResult(sum);
|
190
|
-
}
|
191
|
-
};
|
192
|
-
|
193
|
-
return (
|
194
|
-
<div>
|
195
|
-
<button onClick={calculate}>Calculate 5 + 7</button>
|
196
|
-
<p>Result: {result}</p>
|
197
|
-
</div>
|
198
|
-
);
|
199
|
-
}
|
75
|
+
render(<App />, document.getElementById('root'));
|
200
76
|
```
|
201
77
|
|
202
|
-
##
|
203
|
-
|
204
|
-
### Hooks System
|
205
|
-
|
206
|
-
<details>
|
207
|
-
<summary><b>State Management with useState</b></summary>
|
208
|
-
|
209
|
-
```jsx
|
210
|
-
import { useState } from 'frontend-hamroun';
|
211
|
-
|
212
|
-
function Counter() {
|
213
|
-
const [count, setCount] = useState(0);
|
214
|
-
|
215
|
-
function increment() {
|
216
|
-
setCount(count + 1);
|
217
|
-
}
|
218
|
-
|
219
|
-
function decrement() {
|
220
|
-
setCount(count - 1);
|
221
|
-
}
|
222
|
-
|
223
|
-
// Functional updates for derived state
|
224
|
-
function double() {
|
225
|
-
setCount(prevCount => prevCount * 2);
|
226
|
-
}
|
227
|
-
|
228
|
-
return (
|
229
|
-
<div>
|
230
|
-
<h2>Count: {count}</h2>
|
231
|
-
<button onClick={increment}>+</button>
|
232
|
-
<button onClick={decrement}>-</button>
|
233
|
-
<button onClick={double}>×2</button>
|
234
|
-
</div>
|
235
|
-
);
|
236
|
-
}
|
237
|
-
```
|
238
|
-
</details>
|
78
|
+
## 🔧 API Reference
|
239
79
|
|
240
|
-
|
241
|
-
<summary><b>Side Effects with useEffect</b></summary>
|
80
|
+
### Core Functions
|
242
81
|
|
243
|
-
```
|
244
|
-
|
82
|
+
```typescript
|
83
|
+
// Client-side rendering
|
84
|
+
render(element: JSX.Element, container: HTMLElement): void
|
245
85
|
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
useEffect(() => {
|
251
|
-
// Reset state when userId changes
|
252
|
-
setLoading(true);
|
253
|
-
|
254
|
-
// Fetch user data
|
255
|
-
fetch(`/api/users/${userId}`)
|
256
|
-
.then(res => res.json())
|
257
|
-
.then(data => {
|
258
|
-
setUser(data);
|
259
|
-
setLoading(false);
|
260
|
-
})
|
261
|
-
.catch(err => {
|
262
|
-
console.error(err);
|
263
|
-
setLoading(false);
|
264
|
-
});
|
265
|
-
|
266
|
-
// Cleanup function runs on component unmount or before effect re-runs
|
267
|
-
return () => {
|
268
|
-
// Cancel any pending requests or subscriptions
|
269
|
-
console.log('Cleaning up effect for userId:', userId);
|
270
|
-
};
|
271
|
-
}, [userId]); // Only re-run when userId changes
|
272
|
-
|
273
|
-
if (loading) return <div>Loading...</div>;
|
274
|
-
if (!user) return <div>User not found</div>;
|
275
|
-
|
276
|
-
return (
|
277
|
-
<div>
|
278
|
-
<h1>{user.name}</h1>
|
279
|
-
<p>Email: {user.email}</p>
|
280
|
-
</div>
|
281
|
-
);
|
282
|
-
}
|
86
|
+
// Server-side rendering + hydration
|
87
|
+
renderToString(element: JSX.Element): Promise<string>
|
88
|
+
hydrate(element: JSX.Element, container: HTMLElement): void
|
283
89
|
```
|
284
|
-
</details>
|
285
90
|
|
286
|
-
|
287
|
-
<summary><b>Performance Optimization with useMemo and useRef</b></summary>
|
91
|
+
### Hooks
|
288
92
|
|
289
|
-
```
|
290
|
-
|
93
|
+
```typescript
|
94
|
+
// State management
|
95
|
+
const [state, setState] = useState(initialValue);
|
96
|
+
const ref = useRef(initialValue);
|
291
97
|
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
console.log('Filtering items...');
|
298
|
-
return items.filter(item => item.name.includes(filter));
|
299
|
-
}, [items, filter]);
|
300
|
-
|
301
|
-
// Create a persistent reference that doesn't trigger re-renders
|
302
|
-
const lastRenderTime = useRef(Date.now());
|
303
|
-
|
304
|
-
console.log(`Time since last render: ${Date.now() - lastRenderTime.current}ms`);
|
305
|
-
lastRenderTime.current = Date.now();
|
306
|
-
|
307
|
-
// DOM element references
|
308
|
-
const listRef = useRef(null);
|
309
|
-
|
310
|
-
function scrollToTop() {
|
311
|
-
listRef.current.scrollTop = 0;
|
312
|
-
}
|
313
|
-
|
314
|
-
return (
|
315
|
-
<div>
|
316
|
-
<button onClick={scrollToTop}>Scroll to top</button>
|
317
|
-
<div ref={listRef} style={{ height: '200px', overflow: 'auto' }}>
|
318
|
-
{filteredItems.map(item => (
|
319
|
-
<div
|
320
|
-
key={item.id}
|
321
|
-
onClick={() => setSelectedId(item.id)}
|
322
|
-
style={{
|
323
|
-
background: item.id === selectedId ? 'lightblue' : 'white'
|
324
|
-
}}
|
325
|
-
>
|
326
|
-
{item.name}
|
327
|
-
</div>
|
328
|
-
))}
|
329
|
-
</div>
|
330
|
-
</div>
|
331
|
-
);
|
332
|
-
}
|
333
|
-
```
|
334
|
-
</details>
|
335
|
-
|
336
|
-
<details>
|
337
|
-
<summary><b>Context API for State Management</b></summary>
|
338
|
-
|
339
|
-
```jsx
|
340
|
-
import { createContext, useContext, useState } from 'frontend-hamroun';
|
341
|
-
|
342
|
-
// Create a context with default value
|
343
|
-
const ThemeContext = createContext('light');
|
344
|
-
|
345
|
-
// Provider component to supply context value
|
346
|
-
function ThemeProvider({ children }) {
|
347
|
-
const [theme, setTheme] = useState('light');
|
348
|
-
|
349
|
-
const toggleTheme = () => {
|
350
|
-
setTheme(theme === 'light' ? 'dark' : 'light');
|
98
|
+
// Side effects
|
99
|
+
useEffect(() => {
|
100
|
+
// Effect code
|
101
|
+
return () => {
|
102
|
+
// Cleanup
|
351
103
|
};
|
352
|
-
|
353
|
-
return (
|
354
|
-
<ThemeContext.Provider value={{ theme, toggleTheme }}>
|
355
|
-
{children}
|
356
|
-
</ThemeContext.Provider>
|
357
|
-
);
|
358
|
-
}
|
359
|
-
|
360
|
-
// Consumer component using the context value
|
361
|
-
function ThemedButton() {
|
362
|
-
const { theme, toggleTheme } = useContext(ThemeContext);
|
363
|
-
|
364
|
-
return (
|
365
|
-
<button
|
366
|
-
onClick={toggleTheme}
|
367
|
-
style={{
|
368
|
-
background: theme === 'light' ? '#fff' : '#333',
|
369
|
-
color: theme === 'light' ? '#333' : '#fff',
|
370
|
-
border: '1px solid #ccc',
|
371
|
-
padding: '8px 16px',
|
372
|
-
}}
|
373
|
-
>
|
374
|
-
Switch to {theme === 'light' ? 'dark' : 'light'} mode
|
375
|
-
</button>
|
376
|
-
);
|
377
|
-
}
|
378
|
-
|
379
|
-
// Usage in application
|
380
|
-
function App() {
|
381
|
-
return (
|
382
|
-
<ThemeProvider>
|
383
|
-
<div>
|
384
|
-
<h1>Themed Application</h1>
|
385
|
-
<ThemedButton />
|
386
|
-
</div>
|
387
|
-
</ThemeProvider>
|
388
|
-
);
|
389
|
-
}
|
390
|
-
```
|
391
|
-
</details>
|
392
|
-
|
393
|
-
<details>
|
394
|
-
<summary><b>Error Boundaries with useErrorBoundary</b></summary>
|
395
|
-
|
396
|
-
```jsx
|
397
|
-
import { useErrorBoundary } from 'frontend-hamroun';
|
398
|
-
|
399
|
-
function ErrorBoundary({ children }) {
|
400
|
-
const [error, resetError] = useErrorBoundary();
|
401
|
-
|
402
|
-
if (error) {
|
403
|
-
return (
|
404
|
-
<div className="error-boundary">
|
405
|
-
<h2>Something went wrong</h2>
|
406
|
-
<p>{error.message}</p>
|
407
|
-
<button onClick={resetError}>Try again</button>
|
408
|
-
</div>
|
409
|
-
);
|
410
|
-
}
|
411
|
-
|
412
|
-
return children;
|
413
|
-
}
|
414
|
-
|
415
|
-
// Usage
|
416
|
-
function App() {
|
417
|
-
return (
|
418
|
-
<ErrorBoundary>
|
419
|
-
<UserProfile userId="123" />
|
420
|
-
</ErrorBoundary>
|
421
|
-
);
|
422
|
-
}
|
423
|
-
```
|
424
|
-
</details>
|
425
|
-
|
426
|
-
### Batch Updates for Efficiency
|
427
|
-
|
428
|
-
Frontend Hamroun automatically batches state updates within event handlers and can manually batch other updates with `batchUpdates`:
|
429
|
-
|
430
|
-
```jsx
|
431
|
-
import { useState, batchUpdates } from 'frontend-hamroun';
|
432
|
-
|
433
|
-
function Form() {
|
434
|
-
const [name, setName] = useState('');
|
435
|
-
const [email, setEmail] = useState('');
|
436
|
-
const [isSubmitting, setSubmitting] = useState(false);
|
437
|
-
const [errors, setErrors] = useState({});
|
438
|
-
|
439
|
-
async function handleSubmit(e) {
|
440
|
-
e.preventDefault();
|
441
|
-
|
442
|
-
// Group multiple state updates into a single render
|
443
|
-
batchUpdates(() => {
|
444
|
-
setSubmitting(true);
|
445
|
-
setErrors({});
|
446
|
-
});
|
447
|
-
|
448
|
-
try {
|
449
|
-
const response = await fetch('/api/users', {
|
450
|
-
method: 'POST',
|
451
|
-
headers: { 'Content-Type': 'application/json' },
|
452
|
-
body: JSON.stringify({ name, email })
|
453
|
-
});
|
454
|
-
|
455
|
-
const result = await response.json();
|
456
|
-
|
457
|
-
batchUpdates(() => {
|
458
|
-
setSubmitting(false);
|
459
|
-
setName('');
|
460
|
-
setEmail('');
|
461
|
-
});
|
462
|
-
} catch (error) {
|
463
|
-
batchUpdates(() => {
|
464
|
-
setSubmitting(false);
|
465
|
-
setErrors({ submit: error.message });
|
466
|
-
});
|
467
|
-
}
|
468
|
-
}
|
469
|
-
|
470
|
-
return (
|
471
|
-
<form onSubmit={handleSubmit}>
|
472
|
-
{/* Form fields */}
|
473
|
-
<button type="submit" disabled={isSubmitting}>
|
474
|
-
{isSubmitting ? 'Submitting...' : 'Submit'}
|
475
|
-
</button>
|
476
|
-
</form>
|
477
|
-
);
|
478
|
-
}
|
479
|
-
```
|
480
|
-
|
481
|
-
## 🖥️ Backend Features
|
482
|
-
|
483
|
-
### Express Server Integration
|
484
|
-
|
485
|
-
<details>
|
486
|
-
<summary><b>Server Setup</b></summary>
|
487
|
-
|
488
|
-
```js
|
489
|
-
import { server } from 'frontend-hamroun/server';
|
490
|
-
|
491
|
-
const app = await server.createServer({
|
492
|
-
port: 3000,
|
493
|
-
apiDir: './api', // Directory for API routes
|
494
|
-
pagesDir: './pages', // Directory for page components
|
495
|
-
staticDir: './public', // Directory for static files
|
496
|
-
|
497
|
-
// Database configuration
|
498
|
-
db: {
|
499
|
-
url: process.env.DATABASE_URL,
|
500
|
-
type: 'mongodb' // mongodb, mysql, or postgres
|
501
|
-
},
|
502
|
-
|
503
|
-
// Authentication configuration
|
504
|
-
auth: {
|
505
|
-
secret: process.env.JWT_SECRET,
|
506
|
-
expiresIn: '7d' // Token expiration time
|
507
|
-
}
|
508
|
-
});
|
509
|
-
|
510
|
-
await app.start();
|
511
|
-
console.log('Server running at http://localhost:3000');
|
512
|
-
```
|
513
|
-
</details>
|
514
|
-
|
515
|
-
### File-Based API Routing
|
516
|
-
|
517
|
-
<details>
|
518
|
-
<summary><b>API Routes</b></summary>
|
519
|
-
|
520
|
-
```js
|
521
|
-
// api/users.js (automatically maps to /api/users)
|
522
|
-
export async function get(req, res) {
|
523
|
-
// GET /api/users - List all users
|
524
|
-
const users = await req.db.collection('users').find().toArray();
|
525
|
-
res.json(users);
|
526
|
-
}
|
527
|
-
|
528
|
-
export async function post(req, res) {
|
529
|
-
// POST /api/users - Create a new user
|
530
|
-
const { name, email } = req.body;
|
531
|
-
|
532
|
-
if (!name || !email) {
|
533
|
-
return res.status(400).json({ error: 'Name and email are required' });
|
534
|
-
}
|
535
|
-
|
536
|
-
const result = await req.db.collection('users').insertOne({
|
537
|
-
name,
|
538
|
-
email,
|
539
|
-
createdAt: new Date()
|
540
|
-
});
|
541
|
-
|
542
|
-
res.status(201).json({ id: result.insertedId });
|
543
|
-
}
|
544
|
-
```
|
104
|
+
}, [dependencies]);
|
545
105
|
|
546
|
-
|
547
|
-
|
548
|
-
export async function get(req, res) {
|
549
|
-
// GET /api/users/:id - Get user by ID
|
550
|
-
const { id } = req.params;
|
551
|
-
|
552
|
-
try {
|
553
|
-
const user = await req.db.collection('users').findOne({
|
554
|
-
_id: new ObjectId(id)
|
555
|
-
});
|
556
|
-
|
557
|
-
if (!user) {
|
558
|
-
return res.status(404).json({ error: 'User not found' });
|
559
|
-
}
|
560
|
-
|
561
|
-
res.json(user);
|
562
|
-
} catch (error) {
|
563
|
-
res.status(500).json({ error: 'Server error' });
|
564
|
-
}
|
565
|
-
}
|
566
|
-
|
567
|
-
export async function put(req, res) {
|
568
|
-
// PUT /api/users/:id - Update user
|
569
|
-
const { id } = req.params;
|
570
|
-
const { name, email } = req.body;
|
571
|
-
|
572
|
-
await req.db.collection('users').updateOne(
|
573
|
-
{ _id: new ObjectId(id) },
|
574
|
-
{ $set: { name, email, updatedAt: new Date() } }
|
575
|
-
);
|
576
|
-
|
577
|
-
res.json({ success: true });
|
578
|
-
}
|
106
|
+
// Performance
|
107
|
+
const memoized = useMemo(() => computation(), [deps]);
|
579
108
|
|
580
|
-
|
581
|
-
|
582
|
-
// Note: 'delete' is a reserved word, so we use 'del'
|
583
|
-
const { id } = req.params;
|
584
|
-
|
585
|
-
await req.db.collection('users').deleteOne({
|
586
|
-
_id: new ObjectId(id)
|
587
|
-
});
|
588
|
-
|
589
|
-
res.status(204).end();
|
590
|
-
}
|
109
|
+
// Error handling
|
110
|
+
const [error, resetError] = useErrorBoundary();
|
591
111
|
```
|
592
|
-
</details>
|
593
112
|
|
594
|
-
###
|
113
|
+
### Context API
|
595
114
|
|
596
|
-
|
597
|
-
|
115
|
+
```typescript
|
116
|
+
const Context = createContext(defaultValue);
|
117
|
+
const value = useContext(Context);
|
598
118
|
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
const posts = await req.db.collection('posts')
|
604
|
-
.aggregate([
|
605
|
-
{ $match: { published: true } },
|
606
|
-
{ $sort: { createdAt: -1 } },
|
607
|
-
{ $limit: 10 },
|
608
|
-
{ $lookup: {
|
609
|
-
from: 'users',
|
610
|
-
localField: 'authorId',
|
611
|
-
foreignField: '_id',
|
612
|
-
as: 'author'
|
613
|
-
}},
|
614
|
-
{ $unwind: '$author' },
|
615
|
-
{ $project: {
|
616
|
-
title: 1,
|
617
|
-
content: 1,
|
618
|
-
createdAt: 1,
|
619
|
-
'author.name': 1,
|
620
|
-
'author.email': 1
|
621
|
-
}}
|
622
|
-
])
|
623
|
-
.toArray();
|
624
|
-
|
625
|
-
res.json(posts);
|
626
|
-
}
|
119
|
+
// Provider
|
120
|
+
<Context.Provider value={value}>
|
121
|
+
{children}
|
122
|
+
</Context.Provider>
|
627
123
|
```
|
628
|
-
</details>
|
629
|
-
|
630
|
-
<details>
|
631
|
-
<summary><b>MySQL Example</b></summary>
|
632
|
-
|
633
|
-
```js
|
634
|
-
// api/products.js
|
635
|
-
export async function get(req, res) {
|
636
|
-
// Complex SQL query with joins
|
637
|
-
const [products] = await req.db.execute(`
|
638
|
-
SELECT
|
639
|
-
p.id,
|
640
|
-
p.name,
|
641
|
-
p.price,
|
642
|
-
c.name as categoryName
|
643
|
-
FROM
|
644
|
-
products p
|
645
|
-
JOIN
|
646
|
-
categories c ON p.category_id = c.id
|
647
|
-
WHERE
|
648
|
-
p.active = ?
|
649
|
-
ORDER BY
|
650
|
-
p.created_at DESC
|
651
|
-
LIMIT 20
|
652
|
-
`, [true]);
|
653
|
-
|
654
|
-
res.json(products);
|
655
|
-
}
|
656
|
-
```
|
657
|
-
</details>
|
658
|
-
|
659
|
-
<details>
|
660
|
-
<summary><b>PostgreSQL Example</b></summary>
|
661
|
-
|
662
|
-
```js
|
663
|
-
// api/analytics.js
|
664
|
-
export async function get(req, res) {
|
665
|
-
// Advanced PostgreSQL features
|
666
|
-
const result = await req.db.query(`
|
667
|
-
WITH monthly_sales AS (
|
668
|
-
SELECT
|
669
|
-
date_trunc('month', order_date) as month,
|
670
|
-
SUM(total_amount) as revenue
|
671
|
-
FROM
|
672
|
-
orders
|
673
|
-
WHERE
|
674
|
-
order_date > NOW() - INTERVAL '1 year'
|
675
|
-
GROUP BY
|
676
|
-
date_trunc('month', order_date)
|
677
|
-
)
|
678
|
-
SELECT
|
679
|
-
month,
|
680
|
-
revenue,
|
681
|
-
lag(revenue) OVER (ORDER BY month) as prev_month_revenue,
|
682
|
-
round((revenue - lag(revenue) OVER (ORDER BY month)) /
|
683
|
-
lag(revenue) OVER (ORDER BY month) * 100, 2) as growth_percent
|
684
|
-
FROM
|
685
|
-
monthly_sales
|
686
|
-
ORDER BY
|
687
|
-
month
|
688
|
-
`);
|
689
|
-
|
690
|
-
res.json(result.rows);
|
691
|
-
}
|
692
|
-
```
|
693
|
-
</details>
|
694
|
-
|
695
|
-
### Authentication System
|
696
124
|
|
697
|
-
|
698
|
-
<summary><b>JWT Authentication</b></summary>
|
699
|
-
|
700
|
-
```js
|
701
|
-
// api/auth/login.js
|
702
|
-
export async function post(req, res) {
|
703
|
-
const { email, password } = req.body;
|
704
|
-
|
705
|
-
// Validate input
|
706
|
-
if (!email || !password) {
|
707
|
-
return res.status(400).json({ error: 'Email and password are required' });
|
708
|
-
}
|
709
|
-
|
710
|
-
try {
|
711
|
-
// Find user by email
|
712
|
-
const user = await req.db.collection('users').findOne({ email });
|
713
|
-
|
714
|
-
// Check if user exists and password is correct
|
715
|
-
if (!user || !await req.auth.comparePasswords(password, user.password)) {
|
716
|
-
return res.status(401).json({ error: 'Invalid credentials' });
|
717
|
-
}
|
718
|
-
|
719
|
-
// Generate JWT token
|
720
|
-
const token = req.auth.generateToken({
|
721
|
-
id: user._id,
|
722
|
-
name: user.name,
|
723
|
-
email: user.email,
|
724
|
-
roles: user.roles || ['user']
|
725
|
-
});
|
726
|
-
|
727
|
-
// Return token and user info (excluding sensitive data)
|
728
|
-
res.json({
|
729
|
-
token,
|
730
|
-
user: {
|
731
|
-
id: user._id,
|
732
|
-
name: user.name,
|
733
|
-
email: user.email,
|
734
|
-
roles: user.roles || ['user']
|
735
|
-
}
|
736
|
-
});
|
737
|
-
} catch (error) {
|
738
|
-
res.status(500).json({ error: 'Authentication failed' });
|
739
|
-
}
|
740
|
-
}
|
741
|
-
```
|
742
|
-
|
743
|
-
```js
|
744
|
-
// api/auth/register.js
|
745
|
-
export async function post(req, res) {
|
746
|
-
const { name, email, password } = req.body;
|
747
|
-
|
748
|
-
// Validate input
|
749
|
-
if (!name || !email || !password) {
|
750
|
-
return res.status(400).json({
|
751
|
-
error: 'Name, email, and password are required'
|
752
|
-
});
|
753
|
-
}
|
754
|
-
|
755
|
-
try {
|
756
|
-
// Check if user already exists
|
757
|
-
const existingUser = await req.db.collection('users').findOne({ email });
|
758
|
-
if (existingUser) {
|
759
|
-
return res.status(409).json({ error: 'Email already in use' });
|
760
|
-
}
|
761
|
-
|
762
|
-
// Hash password
|
763
|
-
const hashedPassword = await req.auth.hashPassword(password);
|
764
|
-
|
765
|
-
// Create user
|
766
|
-
const result = await req.db.collection('users').insertOne({
|
767
|
-
name,
|
768
|
-
email,
|
769
|
-
password: hashedPassword,
|
770
|
-
roles: ['user'],
|
771
|
-
createdAt: new Date()
|
772
|
-
});
|
773
|
-
|
774
|
-
res.status(201).json({
|
775
|
-
id: result.insertedId,
|
776
|
-
name,
|
777
|
-
email,
|
778
|
-
roles: ['user']
|
779
|
-
});
|
780
|
-
} catch (error) {
|
781
|
-
res.status(500).json({ error: 'Registration failed' });
|
782
|
-
}
|
783
|
-
}
|
784
|
-
```
|
785
|
-
|
786
|
-
```js
|
787
|
-
// middleware/requireAuth.js - Protected route middleware
|
788
|
-
export default function requireAuth(req, res, next) {
|
789
|
-
const token = req.headers.authorization?.split(' ')[1];
|
790
|
-
|
791
|
-
if (!token) {
|
792
|
-
return res.status(401).json({ error: 'Authentication required' });
|
793
|
-
}
|
794
|
-
|
795
|
-
try {
|
796
|
-
// Verify token
|
797
|
-
const decoded = req.auth.verifyToken(token);
|
798
|
-
req.user = decoded; // Attach user to request
|
799
|
-
next();
|
800
|
-
} catch (error) {
|
801
|
-
return res.status(401).json({ error: 'Invalid or expired token' });
|
802
|
-
}
|
803
|
-
}
|
804
|
-
|
805
|
-
// api/profile.js - Protected route example
|
806
|
-
import requireAuth from '../middleware/requireAuth.js';
|
807
|
-
|
808
|
-
export const middleware = [requireAuth];
|
809
|
-
|
810
|
-
export async function get(req, res) {
|
811
|
-
// req.user is available from requireAuth middleware
|
812
|
-
const { id } = req.user;
|
813
|
-
|
814
|
-
const profile = await req.db.collection('users').findOne(
|
815
|
-
{ _id: new ObjectId(id) },
|
816
|
-
{ projection: { password: 0 } } // Exclude password
|
817
|
-
);
|
818
|
-
|
819
|
-
res.json(profile);
|
820
|
-
}
|
821
|
-
```
|
822
|
-
</details>
|
823
|
-
|
824
|
-
## 📦 WebAssembly Integration
|
825
|
-
|
826
|
-
Frontend Hamroun includes built-in support for Go WebAssembly modules, allowing you to leverage Go's performance benefits in your JavaScript applications:
|
827
|
-
|
828
|
-
<details>
|
829
|
-
<summary><b>Loading Go WASM Modules</b></summary>
|
830
|
-
|
831
|
-
```jsx
|
832
|
-
import { useState, useEffect } from 'frontend-hamroun';
|
833
|
-
import { loadGoWasm } from 'frontend-hamroun';
|
834
|
-
|
835
|
-
function GoWasmComponent() {
|
836
|
-
const [wasmModule, setWasmModule] = useState(null);
|
837
|
-
const [loading, setLoading] = useState(true);
|
838
|
-
const [error, setError] = useState(null);
|
839
|
-
const [result, setResult] = useState(null);
|
840
|
-
|
841
|
-
// Load the WASM module on component mount
|
842
|
-
useEffect(() => {
|
843
|
-
async function loadWasmModule() {
|
844
|
-
try {
|
845
|
-
const instance = await loadGoWasm('/go-module.wasm', {
|
846
|
-
goWasmPath: '/wasm_exec.js', // Path to the Go runtime JS
|
847
|
-
debug: true // Enable debug logging
|
848
|
-
});
|
849
|
-
|
850
|
-
setWasmModule(instance);
|
851
|
-
setLoading(false);
|
852
|
-
} catch (err) {
|
853
|
-
console.error('Failed to load WASM module:', err);
|
854
|
-
setError(err.message);
|
855
|
-
setLoading(false);
|
856
|
-
}
|
857
|
-
}
|
858
|
-
|
859
|
-
loadWasmModule();
|
860
|
-
}, []);
|
861
|
-
|
862
|
-
// Call a Go function
|
863
|
-
const handleCalculate = () => {
|
864
|
-
if (!wasmModule) return;
|
865
|
-
|
866
|
-
// Call the Go function
|
867
|
-
const sum = wasmModule.functions.goAdd(5, 7);
|
868
|
-
setResult(sum);
|
869
|
-
};
|
870
|
-
|
871
|
-
if (loading) return <div>Loading WebAssembly module...</div>;
|
872
|
-
if (error) return <div>Error: {error}</div>;
|
873
|
-
|
874
|
-
return (
|
875
|
-
<div>
|
876
|
-
<h2>Go WebAssembly Example</h2>
|
877
|
-
<button onClick={handleCalculate}>Calculate 5 + 7</button>
|
878
|
-
{result !== null && <p>Result: {result}</p>}
|
879
|
-
</div>
|
880
|
-
);
|
881
|
-
}
|
882
|
-
```
|
883
|
-
</details>
|
884
|
-
|
885
|
-
<details>
|
886
|
-
<summary><b>Creating Go WASM Modules</b></summary>
|
887
|
-
|
888
|
-
```go
|
889
|
-
// example.go
|
890
|
-
//go:build js && wasm
|
891
|
-
// +build js,wasm
|
892
|
-
|
893
|
-
package main
|
894
|
-
|
895
|
-
import (
|
896
|
-
"encoding/json"
|
897
|
-
"fmt"
|
898
|
-
"syscall/js"
|
899
|
-
)
|
900
|
-
|
901
|
-
// Function to be called from JavaScript
|
902
|
-
func add(this js.Value, args []js.Value) interface{} {
|
903
|
-
if len(args) != 2 {
|
904
|
-
return js.ValueOf("Error: Expected two arguments")
|
905
|
-
}
|
906
|
-
|
907
|
-
a := args[0].Int()
|
908
|
-
b := args[1].Int()
|
909
|
-
return js.ValueOf(a + b)
|
910
|
-
}
|
911
|
-
|
912
|
-
// Processing complex data
|
913
|
-
func processData(this js.Value, args []js.Value) interface{} {
|
914
|
-
if len(args) == 0 {
|
915
|
-
return js.ValueOf("Error: Expected at least one argument")
|
916
|
-
}
|
917
|
-
|
918
|
-
// Get input data as JS object
|
919
|
-
data := args[0]
|
920
|
-
jsonStr := js.Global().Get("JSON").Call("stringify", data).String()
|
921
|
-
|
922
|
-
// Parse JSON into Go structure
|
923
|
-
var inputMap map[string]interface{}
|
924
|
-
json.Unmarshal([]byte(jsonStr), &inputMap)
|
925
|
-
|
926
|
-
// Process data
|
927
|
-
inputMap["processed"] = true
|
928
|
-
inputMap["processor"] = "Go WASM"
|
929
|
-
|
930
|
-
// Add computed values
|
931
|
-
if values, ok := inputMap["values"].([]interface{}); ok {
|
932
|
-
sum := 0.0
|
933
|
-
for _, v := range values {
|
934
|
-
if num, ok := v.(float64); ok {
|
935
|
-
sum += num
|
936
|
-
}
|
937
|
-
}
|
938
|
-
inputMap["sum"] = sum
|
939
|
-
}
|
940
|
-
|
941
|
-
// Convert back to JSON
|
942
|
-
resultJSON, _ := json.Marshal(inputMap)
|
943
|
-
return js.ValueOf(string(resultJSON))
|
944
|
-
}
|
945
|
-
|
946
|
-
func main() {
|
947
|
-
// Register functions to be callable from JavaScript
|
948
|
-
js.Global().Set("goAdd", js.FuncOf(add))
|
949
|
-
js.Global().Set("goProcessData", js.FuncOf(processData))
|
950
|
-
|
951
|
-
// Keep the program running
|
952
|
-
<-make(chan bool)
|
953
|
-
}
|
954
|
-
```
|
955
|
-
|
956
|
-
```bash
|
957
|
-
# Compile the Go code to WebAssembly
|
958
|
-
GOOS=js GOARCH=wasm go build -o public/go-module.wasm example.go
|
959
|
-
|
960
|
-
# On Windows
|
961
|
-
set GOOS=js
|
962
|
-
set GOARCH=wasm
|
963
|
-
go build -o public/go-module.wasm example.go
|
964
|
-
```
|
965
|
-
|
966
|
-
```bash
|
967
|
-
# Copy the necessary Go WASM runtime
|
968
|
-
cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" public/
|
969
|
-
```
|
970
|
-
</details>
|
971
|
-
|
972
|
-
<details>
|
973
|
-
<summary><b>Handling Complex Data with Go WASM</b></summary>
|
974
|
-
|
975
|
-
```jsx
|
976
|
-
import { useState, useEffect } from 'frontend-hamroun';
|
977
|
-
import { loadGoWasm, createTypedWasmFunction } from 'frontend-hamroun';
|
978
|
-
|
979
|
-
function DataProcessor() {
|
980
|
-
const [wasmModule, setWasmModule] = useState(null);
|
981
|
-
const [inputData, setInputData] = useState({
|
982
|
-
name: 'Test Data',
|
983
|
-
values: [10, 20, 30, 40, 50],
|
984
|
-
timestamp: new Date().toISOString()
|
985
|
-
});
|
986
|
-
const [processedData, setProcessedData] = useState(null);
|
987
|
-
|
988
|
-
// Load WASM module
|
989
|
-
useEffect(() => {
|
990
|
-
async function loadModule() {
|
991
|
-
const instance = await loadGoWasm('/go-module.wasm');
|
992
|
-
setWasmModule(instance);
|
993
|
-
}
|
994
|
-
loadModule();
|
995
|
-
}, []);
|
996
|
-
|
997
|
-
// Process data using Go
|
998
|
-
const handleProcessData = () => {
|
999
|
-
if (!wasmModule) return;
|
1000
|
-
|
1001
|
-
// Create a typed function wrapper for better TypeScript support
|
1002
|
-
const processData = createTypedWasmFunction<(data: any) => string>(
|
1003
|
-
wasmModule,
|
1004
|
-
'goProcessData'
|
1005
|
-
);
|
1006
|
-
|
1007
|
-
// Call the Go function
|
1008
|
-
const result = processData(inputData);
|
1009
|
-
setProcessedData(JSON.parse(result));
|
1010
|
-
};
|
1011
|
-
|
1012
|
-
return (
|
1013
|
-
<div>
|
1014
|
-
<h2>Data Processing with Go WASM</h2>
|
1015
|
-
<div>
|
1016
|
-
<h3>Input Data</h3>
|
1017
|
-
<pre>{JSON.stringify(inputData, null, 2)}</pre>
|
1018
|
-
</div>
|
1019
|
-
|
1020
|
-
<button
|
1021
|
-
onClick={handleProcessData}
|
1022
|
-
disabled={!wasmModule}
|
1023
|
-
>
|
1024
|
-
Process Data
|
1025
|
-
</button>
|
1026
|
-
|
1027
|
-
{processedData && (
|
1028
|
-
<div>
|
1029
|
-
<h3>Processed Result</h3>
|
1030
|
-
<pre>{JSON.stringify(processedData, null, 2)}</pre>
|
1031
|
-
</div>
|
1032
|
-
)}
|
1033
|
-
</div>
|
1034
|
-
);
|
1035
|
-
}
|
1036
|
-
```
|
1037
|
-
</details>
|
1038
|
-
|
1039
|
-
<details>
|
1040
|
-
<summary><b>Server-Side WASM Integration</b></summary>
|
1041
|
-
|
1042
|
-
```js
|
1043
|
-
import { loadGoWasmFromFile } from 'frontend-hamroun/server';
|
1044
|
-
import path from 'path';
|
1045
|
-
|
1046
|
-
// API endpoint using Go WASM for computation
|
1047
|
-
export async function get(req, res) {
|
1048
|
-
try {
|
1049
|
-
// Load the WASM module on the server
|
1050
|
-
const wasmPath = path.join(process.cwd(), 'wasm', 'calculator.wasm');
|
1051
|
-
const wasm = await loadGoWasmFromFile(wasmPath);
|
1052
|
-
|
1053
|
-
// Process data using the Go WASM module
|
1054
|
-
const input = {
|
1055
|
-
values: [10, 20, 30, 40, 50],
|
1056
|
-
operation: 'sum'
|
1057
|
-
};
|
1058
|
-
|
1059
|
-
const result = wasm.functions.goProcessData(input);
|
1060
|
-
|
1061
|
-
// Return the processed data
|
1062
|
-
res.json(JSON.parse(result));
|
1063
|
-
} catch (error) {
|
1064
|
-
res.status(500).json({ error: error.message });
|
1065
|
-
}
|
1066
|
-
}
|
1067
|
-
```
|
1068
|
-
</details>
|
1069
|
-
|
1070
|
-
<details>
|
1071
|
-
<summary><b>CLI for Go WASM Development</b></summary>
|
1072
|
-
|
1073
|
-
```bash
|
1074
|
-
# Create a new Go WASM module
|
1075
|
-
npx frontend-hamroun add:wasm calculator
|
1076
|
-
|
1077
|
-
# This will:
|
1078
|
-
# 1. Create calculator.go with basic functions
|
1079
|
-
# 2. Add build scripts for compiling to WASM
|
1080
|
-
# 3. Display usage instructions
|
1081
|
-
|
1082
|
-
# Then compile and use your module
|
1083
|
-
cd src/wasm
|
1084
|
-
./build.sh # or build.bat on Windows
|
1085
|
-
|
1086
|
-
# Import in your code
|
1087
|
-
import { loadGoWasm } from 'frontend-hamroun';
|
1088
|
-
|
1089
|
-
const wasm = await loadGoWasm('/wasm/calculator.wasm');
|
1090
|
-
const result = wasm.functions.goAdd(5, 7);
|
1091
|
-
```
|
1092
|
-
</details>
|
1093
|
-
|
1094
|
-
## 🔄 Server-Side Rendering
|
1095
|
-
|
1096
|
-
Frontend Hamroun provides built-in server-side rendering capabilities to improve performance and SEO:
|
1097
|
-
|
1098
|
-
<details>
|
1099
|
-
<summary><b>Server-Side Rendering Setup</b></summary>
|
1100
|
-
|
1101
|
-
```jsx
|
1102
|
-
// server.js
|
1103
|
-
import express from 'express';
|
1104
|
-
import { renderToString } from 'frontend-hamroun/ssr';
|
1105
|
-
import App from './App';
|
1106
|
-
|
1107
|
-
const app = express();
|
1108
|
-
app.use(express.static('public'));
|
1109
|
-
|
1110
|
-
app.get('*', async (req, res) => {
|
1111
|
-
// Render app to string
|
1112
|
-
const html = await renderToString(<App url={req.url} />);
|
1113
|
-
|
1114
|
-
// Send complete HTML document
|
1115
|
-
res.send(`
|
1116
|
-
<!DOCTYPE html>
|
1117
|
-
<html>
|
1118
|
-
<head>
|
1119
|
-
<title>My SSR App</title>
|
1120
|
-
<meta charset="UTF-8">
|
1121
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
1122
|
-
<link rel="stylesheet" href="/styles.css">
|
1123
|
-
</head>
|
1124
|
-
<body>
|
1125
|
-
<div id="root">${html}</div>
|
1126
|
-
<script src="/bundle.js"></script>
|
1127
|
-
</body>
|
1128
|
-
</html>
|
1129
|
-
`);
|
1130
|
-
});
|
1131
|
-
|
1132
|
-
app.listen(3000, () => {
|
1133
|
-
console.log('Server running at http://localhost:3000');
|
1134
|
-
});
|
1135
|
-
|
1136
|
-
// client.js (for hydration)
|
1137
|
-
import { hydrate } from 'frontend-hamroun';
|
1138
|
-
import App from './App';
|
1139
|
-
|
1140
|
-
// Hydrate the app in the browser
|
1141
|
-
hydrate(<App url={window.location.pathname} />, document.getElementById('root'));
|
1142
|
-
```
|
1143
|
-
</details>
|
1144
|
-
|
1145
|
-
## 🛠️ CLI Tools
|
1146
|
-
|
1147
|
-
Frontend Hamroun includes a powerful CLI for scaffolding projects, components, and API routes:
|
1148
|
-
|
1149
|
-
<details>
|
1150
|
-
<summary><b>Project Creation</b></summary>
|
125
|
+
## 🏭 Production Build
|
1151
126
|
|
1152
127
|
```bash
|
1153
|
-
#
|
1154
|
-
|
128
|
+
# Build for production
|
129
|
+
npm run build
|
1155
130
|
|
1156
|
-
#
|
1157
|
-
|
1158
|
-
```
|
131
|
+
# Start production server
|
132
|
+
npm run start
|
1159
133
|
|
1160
|
-
|
1161
|
-
|
1162
|
-
- `ssr-template`: Server-side rendering with hydration
|
1163
|
-
- `fullstack-app`: Complete solution with frontend, backend, auth, and DB
|
1164
|
-
- `go-wasm-app`: Application with Go WebAssembly integration
|
1165
|
-
</details>
|
1166
|
-
|
1167
|
-
<details>
|
1168
|
-
<summary><b>Component Generation</b></summary>
|
1169
|
-
|
1170
|
-
```bash
|
1171
|
-
# Generate a new component
|
1172
|
-
npx frontend-hamroun add:component Button
|
1173
|
-
|
1174
|
-
# Generate a TypeScript component
|
1175
|
-
npx frontend-hamroun add:component UserProfile --typescript
|
1176
|
-
|
1177
|
-
# Specify path and hooks
|
1178
|
-
npx frontend-hamroun add:component Sidebar --path=src/layout --hooks=useState,useEffect
|
134
|
+
# Build with SSR
|
135
|
+
npm run build:ssr
|
1179
136
|
```
|
1180
|
-
</details>
|
1181
137
|
|
1182
|
-
|
1183
|
-
<summary><b>API Route Generation</b></summary>
|
138
|
+
## 🐳 Docker Support
|
1184
139
|
|
1185
140
|
```bash
|
1186
|
-
#
|
1187
|
-
|
1188
|
-
|
1189
|
-
# Specify HTTP methods and auth requirement
|
1190
|
-
npx frontend-hamroun add:api orders --methods=get,post --auth
|
1191
|
-
```
|
1192
|
-
</details>
|
1193
|
-
|
1194
|
-
<details>
|
1195
|
-
<summary><b>WebAssembly Generation</b></summary>
|
1196
|
-
|
1197
|
-
```bash
|
1198
|
-
# Generate a new Go WASM module
|
1199
|
-
npx frontend-hamroun add:wasm calculator
|
1200
|
-
|
1201
|
-
# Specify path
|
1202
|
-
npx frontend-hamroun add:wasm data-processor --path=src/wasm
|
1203
|
-
|
1204
|
-
# Select module capabilities
|
1205
|
-
npx frontend-hamroun add:wasm math-lib
|
1206
|
-
# (Interactive prompt will ask for module features)
|
1207
|
-
```
|
1208
|
-
</details>
|
1209
|
-
|
1210
|
-
## 🧩 Advanced Usage
|
1211
|
-
|
1212
|
-
<details>
|
1213
|
-
<summary><b>Custom Hooks</b></summary>
|
1214
|
-
|
1215
|
-
```jsx
|
1216
|
-
import { useState, useEffect } from 'frontend-hamroun';
|
1217
|
-
|
1218
|
-
// Custom hook for fetching data
|
1219
|
-
function useFetch(url, options = {}) {
|
1220
|
-
const [data, setData] = useState(null);
|
1221
|
-
const [loading, setLoading] = useState(true);
|
1222
|
-
const [error, setError] = useState(null);
|
1223
|
-
|
1224
|
-
useEffect(() => {
|
1225
|
-
setLoading(true);
|
1226
|
-
setError(null);
|
1227
|
-
|
1228
|
-
fetch(url, options)
|
1229
|
-
.then(response => {
|
1230
|
-
if (!response.ok) {
|
1231
|
-
throw new Error(`HTTP error ${response.status}`);
|
1232
|
-
}
|
1233
|
-
return response.json();
|
1234
|
-
})
|
1235
|
-
.then(json => {
|
1236
|
-
setData(json);
|
1237
|
-
setLoading(false);
|
1238
|
-
})
|
1239
|
-
.catch(err => {
|
1240
|
-
setError(err.message);
|
1241
|
-
setLoading(false);
|
1242
|
-
});
|
1243
|
-
}, [url]);
|
1244
|
-
|
1245
|
-
return { data, loading, error };
|
1246
|
-
}
|
1247
|
-
|
1248
|
-
// Usage
|
1249
|
-
function UserList() {
|
1250
|
-
const { data, loading, error } = useFetch('/api/users');
|
1251
|
-
|
1252
|
-
if (loading) return <div>Loading...</div>;
|
1253
|
-
if (error) return <div>Error: {error}</div>;
|
1254
|
-
|
1255
|
-
return (
|
1256
|
-
<ul>
|
1257
|
-
{data.map(user => (
|
1258
|
-
<li key={user.id}>{user.name}</li>
|
1259
|
-
))}
|
1260
|
-
</ul>
|
1261
|
-
);
|
1262
|
-
}
|
1263
|
-
```
|
1264
|
-
</details>
|
1265
|
-
|
1266
|
-
<details>
|
1267
|
-
<summary><b>Performance Optimization Techniques</b></summary>
|
1268
|
-
|
1269
|
-
```jsx
|
1270
|
-
import { useMemo, useState, useRef, batchUpdates } from 'frontend-hamroun';
|
1271
|
-
|
1272
|
-
function OptimizedList({ items }) {
|
1273
|
-
const [filter, setFilter] = useState('');
|
1274
|
-
const [sortOrder, setSortOrder] = useState('asc');
|
1275
|
-
const prevItems = useRef(items);
|
1276
|
-
|
1277
|
-
// Memoize expensive calculations
|
1278
|
-
const processedItems = useMemo(() => {
|
1279
|
-
console.log('Processing items...');
|
1280
|
-
let result = [...items];
|
1281
|
-
|
1282
|
-
// Apply filter
|
1283
|
-
if (filter) {
|
1284
|
-
result = result.filter(item =>
|
1285
|
-
item.name.toLowerCase().includes(filter.toLowerCase())
|
1286
|
-
);
|
1287
|
-
}
|
1288
|
-
|
1289
|
-
// Apply sorting
|
1290
|
-
result.sort((a, b) => {
|
1291
|
-
const comparison = a.name.localeCompare(b.name);
|
1292
|
-
return sortOrder === 'asc' ? comparison : -comparison;
|
1293
|
-
});
|
1294
|
-
|
1295
|
-
return result;
|
1296
|
-
}, [items, filter, sortOrder]);
|
1297
|
-
|
1298
|
-
// Check for changed items with more control than dependency arrays
|
1299
|
-
if (items !== prevItems.current) {
|
1300
|
-
console.log('Items array reference changed');
|
1301
|
-
prevItems.current = items;
|
1302
|
-
}
|
1303
|
-
|
1304
|
-
// Batch multiple state updates together
|
1305
|
-
const toggleSortAndClear = () => {
|
1306
|
-
batchUpdates(() => {
|
1307
|
-
setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc');
|
1308
|
-
setFilter('');
|
1309
|
-
});
|
1310
|
-
};
|
1311
|
-
|
1312
|
-
return (
|
1313
|
-
<div>
|
1314
|
-
<div>
|
1315
|
-
<input
|
1316
|
-
type="text"
|
1317
|
-
value={filter}
|
1318
|
-
onChange={e => setFilter(e.target.value)}
|
1319
|
-
placeholder="Filter items..."
|
1320
|
-
/>
|
1321
|
-
<button onClick={toggleSortAndClear}>
|
1322
|
-
Toggle Sort ({sortOrder})
|
1323
|
-
</button>
|
1324
|
-
</div>
|
1325
|
-
|
1326
|
-
{/* Using key for efficient list rendering */}
|
1327
|
-
<ul>
|
1328
|
-
{processedItems.map(item => (
|
1329
|
-
<li key={item.id}>{item.name}</li>
|
1330
|
-
))}
|
1331
|
-
</ul>
|
1332
|
-
</div>
|
1333
|
-
);
|
1334
|
-
}
|
1335
|
-
```
|
1336
|
-
</details>
|
1337
|
-
|
1338
|
-
## 📝 TypeScript Support
|
1339
|
-
|
1340
|
-
Frontend Hamroun is built with TypeScript and comes with complete type definitions:
|
1341
|
-
|
1342
|
-
<details>
|
1343
|
-
<summary><b>Type-Safe Component Example</b></summary>
|
1344
|
-
|
1345
|
-
```tsx
|
1346
|
-
import { useState, useEffect } from 'frontend-hamroun';
|
1347
|
-
|
1348
|
-
// Define props interface
|
1349
|
-
interface UserProfileProps {
|
1350
|
-
id: number;
|
1351
|
-
name: string;
|
1352
|
-
email: string;
|
1353
|
-
role: 'admin' | 'user' | 'guest';
|
1354
|
-
onStatusChange?: (id: number, active: boolean) => void;
|
1355
|
-
}
|
1356
|
-
|
1357
|
-
// Define state interface
|
1358
|
-
interface UserProfileState {
|
1359
|
-
isActive: boolean;
|
1360
|
-
isEditing: boolean;
|
1361
|
-
formData: {
|
1362
|
-
name: string;
|
1363
|
-
email: string;
|
1364
|
-
};
|
1365
|
-
}
|
1366
|
-
|
1367
|
-
function UserProfile({
|
1368
|
-
id,
|
1369
|
-
name,
|
1370
|
-
email,
|
1371
|
-
role,
|
1372
|
-
onStatusChange
|
1373
|
-
}: UserProfileProps) {
|
1374
|
-
// Type-safe state
|
1375
|
-
const [state, setState] = useState<UserProfileState>({
|
1376
|
-
isActive: true,
|
1377
|
-
isEditing: false,
|
1378
|
-
formData: {
|
1379
|
-
name,
|
1380
|
-
email
|
1381
|
-
}
|
1382
|
-
});
|
1383
|
-
|
1384
|
-
// Type-safe event handlers
|
1385
|
-
const toggleStatus = (): void => {
|
1386
|
-
const newStatus = !state.isActive;
|
1387
|
-
setState(prev => ({
|
1388
|
-
...prev,
|
1389
|
-
isActive: newStatus
|
1390
|
-
}));
|
1391
|
-
|
1392
|
-
if (onStatusChange) {
|
1393
|
-
onStatusChange(id, newStatus);
|
1394
|
-
}
|
1395
|
-
};
|
1396
|
-
|
1397
|
-
// Type-safe refs
|
1398
|
-
const formRef = useRef<HTMLFormElement>(null);
|
1399
|
-
|
1400
|
-
return (
|
1401
|
-
<div className="user-profile">
|
1402
|
-
<h2>{name}</h2>
|
1403
|
-
<p>Email: {email}</p>
|
1404
|
-
<p>Role: {role}</p>
|
1405
|
-
<p>Status: {state.isActive ? 'Active' : 'Inactive'}</p>
|
1406
|
-
|
1407
|
-
<button onClick={toggleStatus}>
|
1408
|
-
{state.isActive ? 'Deactivate' : 'Activate'}
|
1409
|
-
</button>
|
1410
|
-
|
1411
|
-
{state.isEditing ? (
|
1412
|
-
<form ref={formRef}>
|
1413
|
-
{/* Form fields */}
|
1414
|
-
</form>
|
1415
|
-
) : (
|
1416
|
-
<button onClick={() => setState(prev => ({
|
1417
|
-
...prev,
|
1418
|
-
isEditing: true
|
1419
|
-
}))}>
|
1420
|
-
Edit
|
1421
|
-
</button>
|
1422
|
-
)}
|
1423
|
-
</div>
|
1424
|
-
);
|
1425
|
-
}
|
141
|
+
# Build Docker image
|
142
|
+
docker build -t my-app .
|
1426
143
|
|
1427
|
-
|
1428
|
-
|
1429
|
-
<div>
|
1430
|
-
<UserProfile
|
1431
|
-
id={1}
|
1432
|
-
name="John Doe"
|
1433
|
-
email="john@example.com"
|
1434
|
-
role="admin"
|
1435
|
-
onStatusChange={(id, active) => {
|
1436
|
-
console.log(`User ${id} status changed to ${active}`);
|
1437
|
-
}}
|
1438
|
-
/>
|
1439
|
-
|
1440
|
-
{/* This would cause TypeScript errors */}
|
1441
|
-
{/*
|
1442
|
-
<UserProfile
|
1443
|
-
id="1" // Error: string is not assignable to number
|
1444
|
-
name="Jane Doe"
|
1445
|
-
role="manager" // Error: 'manager' is not assignable
|
1446
|
-
/>
|
1447
|
-
*/}
|
1448
|
-
</div>
|
1449
|
-
);
|
144
|
+
# Run container
|
145
|
+
docker run -p 3000:3000 my-app
|
1450
146
|
```
|
1451
|
-
</details>
|
1452
147
|
|
1453
|
-
|
1454
|
-
<summary><b>Type-Safe API Routes</b></summary>
|
148
|
+
## 📊 Performance
|
1455
149
|
|
1456
|
-
|
1457
|
-
|
1458
|
-
|
1459
|
-
|
1460
|
-
|
1461
|
-
|
1462
|
-
password?: string;
|
1463
|
-
role: 'admin' | 'user';
|
1464
|
-
createdAt?: Date;
|
1465
|
-
updatedAt?: Date;
|
1466
|
-
}
|
150
|
+
| Metric | Frontend Hamroun | React | Vue |
|
151
|
+
|--------|------------------|-------|-----|
|
152
|
+
| Bundle Size (gzipped) | 8.2 KB | 42.2 KB | 36.1 KB |
|
153
|
+
| Initial Render | 12ms | 18ms | 15ms |
|
154
|
+
| Update Performance | 3.2ms | 4.8ms | 4.1ms |
|
155
|
+
| Memory Usage | 2.1 MB | 3.8 MB | 3.2 MB |
|
1467
156
|
|
1468
|
-
|
1469
|
-
import type { User } from '../../types';
|
157
|
+
## 🎯 Use Cases
|
1470
158
|
|
1471
|
-
|
1472
|
-
|
1473
|
-
|
1474
|
-
|
1475
|
-
const user = await req.db.collection('users').findOne({
|
1476
|
-
_id: new ObjectId(id)
|
1477
|
-
}) as User;
|
1478
|
-
|
1479
|
-
if (!user) {
|
1480
|
-
return res.status(404).json({ error: 'User not found' });
|
1481
|
-
}
|
1482
|
-
|
1483
|
-
// Omit sensitive data
|
1484
|
-
const { password, ...safeUser } = user;
|
1485
|
-
|
1486
|
-
res.json(safeUser);
|
1487
|
-
} catch (error) {
|
1488
|
-
res.status(500).json({ error: 'Server error' });
|
1489
|
-
}
|
1490
|
-
}
|
159
|
+
### E-commerce Applications
|
160
|
+
- Product catalogs with SSR for SEO
|
161
|
+
- Shopping cart state management
|
162
|
+
- Real-time inventory updates
|
1491
163
|
|
1492
|
-
|
1493
|
-
|
1494
|
-
|
1495
|
-
|
1496
|
-
// Validate that role is a valid enum value
|
1497
|
-
if (role && !['admin', 'user'].includes(role)) {
|
1498
|
-
return res.status(400).json({ error: 'Invalid role' });
|
1499
|
-
}
|
1500
|
-
|
1501
|
-
try {
|
1502
|
-
await req.db.collection('users').updateOne(
|
1503
|
-
{ _id: new ObjectId(id) },
|
1504
|
-
{
|
1505
|
-
$set: {
|
1506
|
-
...(name && { name }),
|
1507
|
-
...(email && { email }),
|
1508
|
-
...(role && { role }),
|
1509
|
-
updatedAt: new Date()
|
1510
|
-
}
|
1511
|
-
}
|
1512
|
-
);
|
1513
|
-
|
1514
|
-
res.json({ success: true });
|
1515
|
-
} catch (error) {
|
1516
|
-
res.status(500).json({ error: 'Server error' });
|
1517
|
-
}
|
1518
|
-
}
|
1519
|
-
```
|
1520
|
-
</details>
|
1521
|
-
|
1522
|
-
## 🌐 Browser Compatibility
|
1523
|
-
|
1524
|
-
Frontend Hamroun supports all modern browsers out of the box:
|
1525
|
-
|
1526
|
-
- Chrome (latest 2 versions)
|
1527
|
-
- Firefox (latest 2 versions)
|
1528
|
-
- Safari (latest 2 versions)
|
1529
|
-
- Edge (latest 2 versions)
|
1530
|
-
|
1531
|
-
For older browsers like IE11, you'll need:
|
1532
|
-
- Appropriate polyfills
|
1533
|
-
- Transpilation to ES5
|
1534
|
-
- CSS compatibility work
|
1535
|
-
|
1536
|
-
## ❓ Frequently Asked Questions
|
164
|
+
### Content Management Systems
|
165
|
+
- Server-rendered pages for SEO
|
166
|
+
- Rich text editing components
|
167
|
+
- Media management interfaces
|
1537
168
|
|
1538
|
-
|
1539
|
-
|
169
|
+
### Real-time Dashboards
|
170
|
+
- Live data visualization
|
171
|
+
- WebSocket integration
|
172
|
+
- Performance monitoring
|
1540
173
|
|
1541
|
-
|
1542
|
-
- Smaller bundle size (~5KB vs. 42KB+)
|
1543
|
-
- Integrated backend capabilities
|
1544
|
-
- Built-in server-side rendering
|
1545
|
-
- Database integrations
|
1546
|
-
- Authentication system
|
174
|
+
## 🛠️ Development Tools
|
1547
175
|
|
1548
|
-
|
1549
|
-
|
176
|
+
- **Hot Module Replacement** - Instant updates during development
|
177
|
+
- **Error Overlay** - Detailed error reporting with stack traces
|
178
|
+
- **TypeScript Integration** - Full IntelliSense and type checking
|
179
|
+
- **Performance Profiler** - Built-in performance monitoring
|
180
|
+
- **Source Maps** - Easy debugging in development
|
1550
181
|
|
1551
|
-
|
1552
|
-
<summary><b>Can I use it with existing React components?</b></summary>
|
182
|
+
## 🌐 Browser Support
|
1553
183
|
|
1554
|
-
|
1555
|
-
-
|
1556
|
-
-
|
1557
|
-
-
|
1558
|
-
</details>
|
184
|
+
- Chrome 90+
|
185
|
+
- Firefox 88+
|
186
|
+
- Safari 14+
|
187
|
+
- Edge 90+
|
1559
188
|
|
1560
|
-
|
1561
|
-
<summary><b>Does it support static site generation (SSG)?</b></summary>
|
189
|
+
## 📚 Documentation
|
1562
190
|
|
1563
|
-
|
1564
|
-
|
191
|
+
- [API Reference](./docs/API.md)
|
192
|
+
- [Activity Workflows](./docs/main.pdf)
|
193
|
+
- [Contributing Guide](./docs/CONTRIBUTING.md)
|
194
|
+
- [Examples](./examples/)
|
1565
195
|
|
1566
|
-
|
1567
|
-
<summary><b>Why integrate Go with WebAssembly?</b></summary>
|
196
|
+
## 🤝 Contributing
|
1568
197
|
|
1569
|
-
|
1570
|
-
|
1571
|
-
|
1572
|
-
|
1573
|
-
|
1574
|
-
- **Consistent behavior**: Same code runs identically in browser and on server
|
198
|
+
1. Fork the repository
|
199
|
+
2. Create a feature branch: `git checkout -b feature/new-feature`
|
200
|
+
3. Commit changes: `git commit -am 'Add new feature'`
|
201
|
+
4. Push to branch: `git push origin feature/new-feature`
|
202
|
+
5. Submit a Pull Request
|
1575
203
|
|
1576
|
-
|
1577
|
-
</details>
|
1578
|
-
|
1579
|
-
<details>
|
1580
|
-
<summary><b>What's the performance impact of using Go WASM?</b></summary>
|
1581
|
-
|
1582
|
-
While WebAssembly is generally faster than JavaScript for CPU-intensive tasks, there are considerations:
|
1583
|
-
|
1584
|
-
- **Initial load**: WASM modules add to your bundle size (though they can be loaded on-demand)
|
1585
|
-
- **Memory usage**: Go's runtime in WASM uses more memory than hand-written WASM
|
1586
|
-
- **Startup time**: The Go runtime has some initialization overhead
|
1587
|
-
- **Computation speed**: For heavy calculations, Go WASM can be 2-10x faster than equivalent JS
|
204
|
+
## 📄 License
|
1588
205
|
|
1589
|
-
|
1590
|
-
</details>
|
206
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
1591
207
|
|
1592
|
-
##
|
208
|
+
## 🙏 Acknowledgments
|
1593
209
|
|
1594
|
-
|
210
|
+
- Inspired by React's component model
|
211
|
+
- Built with modern web standards
|
212
|
+
- Optimized for developer experience
|
1595
213
|
|
1596
214
|
---
|
1597
215
|
|
1598
|
-
|
1599
|
-
<a href="#-table-of-contents">⬆️ Back to top</a>
|
1600
|
-
</p>
|
216
|
+
**Frontend Hamroun** - Building the future of web development, one component at a time.
|