frontend-hamroun 1.2.63 → 1.2.65

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.
Files changed (2) hide show
  1. package/README.md +1062 -111
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,11 +1,7 @@
1
1
  # Frontend Hamroun
2
2
 
3
3
  <p align="center">
4
- <img src="https://drive.google.com/uc?export=view&id=1zJV5tgl9p23VaNd5YeVF4Wx9v_oTdgZp" alt="Frontend Hamroun Logo" width="300">
5
- </p>
6
-
7
- <p align="center">
8
- <code style="font-size: 24px; color: #4A90E2; background-color: transparent">{ Frontend Hamroun }</code>
4
+ <img src="https://drive.google.com/uc?export=view&id=15VsMSNDhWAfV_R6ZWJltOJd-RMs9UH_y" alt="Frontend Hamroun Logo" width="300">
9
5
  </p>
10
6
 
11
7
  A lightweight full-stack JavaScript framework with Virtual DOM and hooks implementation
@@ -15,21 +11,61 @@ A lightweight full-stack JavaScript framework with Virtual DOM and hooks impleme
15
11
  [![npm](https://img.shields.io/npm/dt/frontend-hamroun)](https://www.npmjs.com/package/frontend-hamroun)
16
12
  [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
17
13
 
18
- ## Installation
14
+ ## 📋 Table of Contents
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
+ - [API Reference](#-api-reference)
23
+ - [CLI Tools](#-cli-tools)
24
+ - [Advanced Usage](#-advanced-usage)
25
+ - [TypeScript Support](#-typescript-support)
26
+ - [Browser Compatibility](#-browser-compatibility)
27
+ - [FAQ](#-frequently-asked-questions)
28
+ - [License](#-license)
29
+
30
+ ## 🚀 Introduction
31
+
32
+ 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.
33
+
34
+ ### Key Features
35
+
36
+ - **Efficient Virtual DOM**: Minimizes DOM operations with intelligent diffing
37
+ - **Complete Hooks API**: useState, useEffect, useRef, useMemo, and more
38
+ - **Full-Stack Solution**: Unified frontend and backend development
39
+ - **Server-Side Rendering**: Optimized SSR with hydration
40
+ - **File-Based Routing**: Intuitive API endpoint creation
41
+ - **Database Integration**: Built-in support for MongoDB, MySQL, PostgreSQL
42
+ - **TypeScript Support**: Full type definitions and excellent DX
43
+ - **Automatic Batch Updates**: Efficient state management
44
+ - **CLI Tooling**: Scaffolding for components, pages, and API routes
45
+
46
+ ## 📦 Installation
19
47
 
20
48
  ```bash
49
+ # Using npm
21
50
  npm install frontend-hamroun
51
+
52
+ # Using yarn
53
+ yarn add frontend-hamroun
54
+
55
+ # Using pnpm
56
+ pnpm add frontend-hamroun
22
57
  ```
23
58
 
24
- ## Quick Start
59
+ ## 🏁 Quick Start
25
60
 
26
- Create a new project using:
61
+ <details>
62
+ <summary><b>Method 1: Create a new project with CLI</b></summary>
27
63
 
28
64
  ```bash
29
- # Using the create-frontend-app command
65
+ # Using npx
30
66
  npx create-frontend-app my-app
31
67
 
32
- # Or using the frontend-hamroun CLI
68
+ # Or with the frontend-hamroun CLI
33
69
  npx frontend-hamroun create my-app
34
70
  ```
35
71
 
@@ -41,256 +77,1171 @@ npm install
41
77
  npm run dev
42
78
  ```
43
79
 
44
- ## Basic Usage
80
+ This will scaffold a new project with all the necessary configuration.
81
+ </details>
82
+
83
+ <details>
84
+ <summary><b>Method 2: Add to an existing project</b></summary>
85
+
86
+ ```bash
87
+ # Install the package
88
+ npm install frontend-hamroun
89
+
90
+ # Import and use in your code
91
+ import { render, useState } from 'frontend-hamroun';
92
+ ```
45
93
 
46
94
  ```jsx
95
+ // Create a simple app
47
96
  import { render, useState } from 'frontend-hamroun';
48
97
 
49
- function App() {
98
+ function Counter() {
50
99
  const [count, setCount] = useState(0);
100
+
51
101
  return (
52
102
  <div>
53
103
  <h1>Count: {count}</h1>
54
- <button onClick={() => setCount(count + 1)}>Increment</button>
104
+ <button onClick={() => setCount(count + 1)}>
105
+ Increment
106
+ </button>
55
107
  </div>
56
108
  );
57
109
  }
58
110
 
59
- render(<App />, document.getElementById('root'));
111
+ render(<Counter />, document.getElementById('root'));
60
112
  ```
113
+ </details>
61
114
 
62
- ## Features
115
+ ## 🧠 Core Concepts
63
116
 
64
- - **Lightweight Core**: <5KB gzipped for essential runtime
65
- - **Full-Stack Solution**: Client and server capabilities in one package
66
- - **Virtual DOM**: Efficient rendering and diffing algorithm
67
- - **Hooks API**: Complete hooks system (useState, useEffect, useMemo, useRef)
68
- - **Context API**: Simple state management across components
69
- - **Server-Side Rendering**: Optimized SSR with hydration
70
- - **Database Integration**: Support for MongoDB, MySQL, and PostgreSQL
71
- - **Authentication**: Built-in JWT authentication system
72
- - **API Routing**: Express-based file system routing
73
- - **TypeScript Support**: Full type definitions included
74
- - **Interactive CLI**: Powerful tools for project scaffolding and component creation
117
+ ### Virtual DOM
75
118
 
76
- ## Client-side Features
119
+ Frontend Hamroun uses a lightweight Virtual DOM implementation to efficiently update the real DOM. It only applies the minimal necessary changes by:
77
120
 
78
- ### Component Development
121
+ ```jsx
122
+ // Virtual DOM diffing occurs automatically
123
+ function App() {
124
+ const [count, setCount] = useState(0);
125
+ return (
126
+ <div>
127
+ <h1>Counter</h1>
128
+ <p>Count: {count}</p> {/* Only this text node updates */}
129
+ <button onClick={() => setCount(count + 1)}>Increment</button>
130
+ </div>
131
+ );
132
+ }
133
+ ```
134
+
135
+ ### Component Model
136
+
137
+ Components are the building blocks of your UI. Each component encapsulates its own logic and rendering:
79
138
 
80
139
  ```jsx
81
- import { useState, useEffect } from 'frontend-hamroun';
140
+ // Function components with hooks
141
+ function Greeting({ name }) {
142
+ // State management
143
+ const [clicked, setClicked] = useState(false);
144
+
145
+ // Side effects
146
+ useEffect(() => {
147
+ document.title = `Hello, ${name}`;
148
+ return () => { document.title = 'App'; };
149
+ }, [name]);
150
+
151
+ return (
152
+ <div>
153
+ <h1>{clicked ? `Thanks, ${name}!` : `Hello, ${name}!`}</h1>
154
+ <button onClick={() => setClicked(true)}>
155
+ {clicked ? 'Clicked!' : 'Click me'}
156
+ </button>
157
+ </div>
158
+ );
159
+ }
160
+ ```
161
+
162
+ ## 🎨 Frontend Features
163
+
164
+ ### Hooks System
165
+
166
+ <details>
167
+ <summary><b>State Management with useState</b></summary>
168
+
169
+ ```jsx
170
+ import { useState } from 'frontend-hamroun';
82
171
 
83
172
  function Counter() {
84
173
  const [count, setCount] = useState(0);
85
174
 
175
+ function increment() {
176
+ setCount(count + 1);
177
+ }
178
+
179
+ function decrement() {
180
+ setCount(count - 1);
181
+ }
182
+
183
+ // Functional updates for derived state
184
+ function double() {
185
+ setCount(prevCount => prevCount * 2);
186
+ }
187
+
188
+ return (
189
+ <div>
190
+ <h2>Count: {count}</h2>
191
+ <button onClick={increment}>+</button>
192
+ <button onClick={decrement}>-</button>
193
+ <button onClick={double}>×2</button>
194
+ </div>
195
+ );
196
+ }
197
+ ```
198
+ </details>
199
+
200
+ <details>
201
+ <summary><b>Side Effects with useEffect</b></summary>
202
+
203
+ ```jsx
204
+ import { useState, useEffect } from 'frontend-hamroun';
205
+
206
+ function UserProfile({ userId }) {
207
+ const [user, setUser] = useState(null);
208
+ const [loading, setLoading] = useState(true);
209
+
86
210
  useEffect(() => {
87
- document.title = `Count: ${count}`;
88
- return () => document.title = 'App';
89
- }, [count]);
211
+ // Reset state when userId changes
212
+ setLoading(true);
213
+
214
+ // Fetch user data
215
+ fetch(`/api/users/${userId}`)
216
+ .then(res => res.json())
217
+ .then(data => {
218
+ setUser(data);
219
+ setLoading(false);
220
+ })
221
+ .catch(err => {
222
+ console.error(err);
223
+ setLoading(false);
224
+ });
225
+
226
+ // Cleanup function runs on component unmount or before effect re-runs
227
+ return () => {
228
+ // Cancel any pending requests or subscriptions
229
+ console.log('Cleaning up effect for userId:', userId);
230
+ };
231
+ }, [userId]); // Only re-run when userId changes
232
+
233
+ if (loading) return <div>Loading...</div>;
234
+ if (!user) return <div>User not found</div>;
90
235
 
91
236
  return (
92
- <div className="counter">
93
- <h2>Counter: {count}</h2>
94
- <button onClick={() => setCount(count + 1)}>+</button>
95
- <button onClick={() => setCount(count - 1)}>-</button>
237
+ <div>
238
+ <h1>{user.name}</h1>
239
+ <p>Email: {user.email}</p>
96
240
  </div>
97
241
  );
98
242
  }
99
243
  ```
244
+ </details>
245
+
246
+ <details>
247
+ <summary><b>Performance Optimization with useMemo and useRef</b></summary>
100
248
 
101
- ### Hooks API
249
+ ```jsx
250
+ import { useState, useMemo, useRef } from 'frontend-hamroun';
102
251
 
103
- - **useState**: Manage component state
104
- - **useEffect**: Handle side effects
105
- - **useMemo**: Memoize expensive calculations
106
- - **useRef**: Reference DOM elements and persist values
107
- - **useErrorBoundary**: Create error boundaries
252
+ function ExpensiveCalculation({ items, filter }) {
253
+ const [selectedId, setSelectedId] = useState(null);
254
+
255
+ // Cache expensive calculation results, only recalculate when dependencies change
256
+ const filteredItems = useMemo(() => {
257
+ console.log('Filtering items...');
258
+ return items.filter(item => item.name.includes(filter));
259
+ }, [items, filter]);
260
+
261
+ // Create a persistent reference that doesn't trigger re-renders
262
+ const lastRenderTime = useRef(Date.now());
263
+
264
+ console.log(`Time since last render: ${Date.now() - lastRenderTime.current}ms`);
265
+ lastRenderTime.current = Date.now();
266
+
267
+ // DOM element references
268
+ const listRef = useRef(null);
269
+
270
+ function scrollToTop() {
271
+ listRef.current.scrollTop = 0;
272
+ }
273
+
274
+ return (
275
+ <div>
276
+ <button onClick={scrollToTop}>Scroll to top</button>
277
+ <div ref={listRef} style={{ height: '200px', overflow: 'auto' }}>
278
+ {filteredItems.map(item => (
279
+ <div
280
+ key={item.id}
281
+ onClick={() => setSelectedId(item.id)}
282
+ style={{
283
+ background: item.id === selectedId ? 'lightblue' : 'white'
284
+ }}
285
+ >
286
+ {item.name}
287
+ </div>
288
+ ))}
289
+ </div>
290
+ </div>
291
+ );
292
+ }
293
+ ```
294
+ </details>
108
295
 
109
- ### Context API
296
+ <details>
297
+ <summary><b>Context API for State Management</b></summary>
110
298
 
111
299
  ```jsx
112
- // Create context
300
+ import { createContext, useContext, useState } from 'frontend-hamroun';
301
+
302
+ // Create a context with default value
113
303
  const ThemeContext = createContext('light');
114
304
 
115
- // Provider
116
- function App() {
305
+ // Provider component to supply context value
306
+ function ThemeProvider({ children }) {
117
307
  const [theme, setTheme] = useState('light');
118
308
 
309
+ const toggleTheme = () => {
310
+ setTheme(theme === 'light' ? 'dark' : 'light');
311
+ };
312
+
119
313
  return (
120
- <ThemeContext.Provider value={theme}>
121
- <Header />
122
- <Main />
123
- <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
124
- Toggle theme
125
- </button>
314
+ <ThemeContext.Provider value={{ theme, toggleTheme }}>
315
+ {children}
126
316
  </ThemeContext.Provider>
127
317
  );
128
318
  }
129
319
 
130
- // Consumer
131
- function Header() {
132
- const theme = useContext(ThemeContext);
133
- return <header className={theme}><h1>My App</h1></header>;
320
+ // Consumer component using the context value
321
+ function ThemedButton() {
322
+ const { theme, toggleTheme } = useContext(ThemeContext);
323
+
324
+ return (
325
+ <button
326
+ onClick={toggleTheme}
327
+ style={{
328
+ background: theme === 'light' ? '#fff' : '#333',
329
+ color: theme === 'light' ? '#333' : '#fff',
330
+ border: '1px solid #ccc',
331
+ padding: '8px 16px',
332
+ }}
333
+ >
334
+ Switch to {theme === 'light' ? 'dark' : 'light'} mode
335
+ </button>
336
+ );
337
+ }
338
+
339
+ // Usage in application
340
+ function App() {
341
+ return (
342
+ <ThemeProvider>
343
+ <div>
344
+ <h1>Themed Application</h1>
345
+ <ThemedButton />
346
+ </div>
347
+ </ThemeProvider>
348
+ );
134
349
  }
135
350
  ```
351
+ </details>
136
352
 
137
- ## Server-Side Features
353
+ <details>
354
+ <summary><b>Error Boundaries with useErrorBoundary</b></summary>
355
+
356
+ ```jsx
357
+ import { useErrorBoundary } from 'frontend-hamroun';
358
+
359
+ function ErrorBoundary({ children }) {
360
+ const [error, resetError] = useErrorBoundary();
361
+
362
+ if (error) {
363
+ return (
364
+ <div className="error-boundary">
365
+ <h2>Something went wrong</h2>
366
+ <p>{error.message}</p>
367
+ <button onClick={resetError}>Try again</button>
368
+ </div>
369
+ );
370
+ }
371
+
372
+ return children;
373
+ }
374
+
375
+ // Usage
376
+ function App() {
377
+ return (
378
+ <ErrorBoundary>
379
+ <UserProfile userId="123" />
380
+ </ErrorBoundary>
381
+ );
382
+ }
383
+ ```
384
+ </details>
385
+
386
+ ### Batch Updates for Efficiency
387
+
388
+ Frontend Hamroun automatically batches state updates within event handlers and can manually batch other updates with `batchUpdates`:
389
+
390
+ ```jsx
391
+ import { useState, batchUpdates } from 'frontend-hamroun';
392
+
393
+ function Form() {
394
+ const [name, setName] = useState('');
395
+ const [email, setEmail] = useState('');
396
+ const [isSubmitting, setSubmitting] = useState(false);
397
+ const [errors, setErrors] = useState({});
398
+
399
+ async function handleSubmit(e) {
400
+ e.preventDefault();
401
+
402
+ // Group multiple state updates into a single render
403
+ batchUpdates(() => {
404
+ setSubmitting(true);
405
+ setErrors({});
406
+ });
407
+
408
+ try {
409
+ const response = await fetch('/api/users', {
410
+ method: 'POST',
411
+ headers: { 'Content-Type': 'application/json' },
412
+ body: JSON.stringify({ name, email })
413
+ });
414
+
415
+ const result = await response.json();
416
+
417
+ batchUpdates(() => {
418
+ setSubmitting(false);
419
+ setName('');
420
+ setEmail('');
421
+ });
422
+ } catch (error) {
423
+ batchUpdates(() => {
424
+ setSubmitting(false);
425
+ setErrors({ submit: error.message });
426
+ });
427
+ }
428
+ }
429
+
430
+ return (
431
+ <form onSubmit={handleSubmit}>
432
+ {/* Form fields */}
433
+ <button type="submit" disabled={isSubmitting}>
434
+ {isSubmitting ? 'Submitting...' : 'Submit'}
435
+ </button>
436
+ </form>
437
+ );
438
+ }
439
+ ```
440
+
441
+ ## 🖥️ Backend Features
138
442
 
139
443
  ### Express Server Integration
140
444
 
445
+ <details>
446
+ <summary><b>Server Setup</b></summary>
447
+
141
448
  ```js
142
449
  import { server } from 'frontend-hamroun/server';
143
450
 
144
451
  const app = await server.createServer({
145
452
  port: 3000,
146
- apiDir: './api',
147
- pagesDir: './pages',
148
- staticDir: './public',
453
+ apiDir: './api', // Directory for API routes
454
+ pagesDir: './pages', // Directory for page components
455
+ staticDir: './public', // Directory for static files
456
+
457
+ // Database configuration
149
458
  db: {
150
459
  url: process.env.DATABASE_URL,
151
- type: 'mongodb' // or 'mysql', 'postgres'
460
+ type: 'mongodb' // mongodb, mysql, or postgres
152
461
  },
462
+
463
+ // Authentication configuration
153
464
  auth: {
154
465
  secret: process.env.JWT_SECRET,
155
- expiresIn: '7d'
466
+ expiresIn: '7d' // Token expiration time
156
467
  }
157
468
  });
158
469
 
159
470
  await app.start();
160
471
  console.log('Server running at http://localhost:3000');
161
472
  ```
473
+ </details>
474
+
475
+ ### File-Based API Routing
162
476
 
163
- ### API Routes
477
+ <details>
478
+ <summary><b>API Routes</b></summary>
164
479
 
165
480
  ```js
166
- // api/users.js - Automatically mapped to /api/users
481
+ // api/users.js (automatically maps to /api/users)
167
482
  export async function get(req, res) {
483
+ // GET /api/users - List all users
168
484
  const users = await req.db.collection('users').find().toArray();
169
485
  res.json(users);
170
486
  }
171
487
 
172
488
  export async function post(req, res) {
489
+ // POST /api/users - Create a new user
173
490
  const { name, email } = req.body;
174
491
 
175
492
  if (!name || !email) {
176
493
  return res.status(400).json({ error: 'Name and email are required' });
177
494
  }
178
495
 
179
- const result = await req.db.collection('users').insertOne({ name, email });
180
- res.status(201).json(result);
496
+ const result = await req.db.collection('users').insertOne({
497
+ name,
498
+ email,
499
+ createdAt: new Date()
500
+ });
501
+
502
+ res.status(201).json({ id: result.insertedId });
181
503
  }
182
504
  ```
183
505
 
184
- ## Server-Side Rendering
506
+ ```js
507
+ // api/users/[id].js (automatically maps to /api/users/:id)
508
+ export async function get(req, res) {
509
+ // GET /api/users/:id - Get user by ID
510
+ const { id } = req.params;
511
+
512
+ try {
513
+ const user = await req.db.collection('users').findOne({
514
+ _id: new ObjectId(id)
515
+ });
516
+
517
+ if (!user) {
518
+ return res.status(404).json({ error: 'User not found' });
519
+ }
520
+
521
+ res.json(user);
522
+ } catch (error) {
523
+ res.status(500).json({ error: 'Server error' });
524
+ }
525
+ }
526
+
527
+ export async function put(req, res) {
528
+ // PUT /api/users/:id - Update user
529
+ const { id } = req.params;
530
+ const { name, email } = req.body;
531
+
532
+ await req.db.collection('users').updateOne(
533
+ { _id: new ObjectId(id) },
534
+ { $set: { name, email, updatedAt: new Date() } }
535
+ );
536
+
537
+ res.json({ success: true });
538
+ }
539
+
540
+ export async function del(req, res) {
541
+ // DELETE /api/users/:id - Delete user
542
+ // Note: 'delete' is a reserved word, so we use 'del'
543
+ const { id } = req.params;
544
+
545
+ await req.db.collection('users').deleteOne({
546
+ _id: new ObjectId(id)
547
+ });
548
+
549
+ res.status(204).end();
550
+ }
551
+ ```
552
+ </details>
553
+
554
+ ### Database Integration
555
+
556
+ <details>
557
+ <summary><b>MongoDB Example</b></summary>
558
+
559
+ ```js
560
+ // api/posts.js
561
+ export async function get(req, res) {
562
+ // Complex MongoDB aggregation
563
+ const posts = await req.db.collection('posts')
564
+ .aggregate([
565
+ { $match: { published: true } },
566
+ { $sort: { createdAt: -1 } },
567
+ { $limit: 10 },
568
+ { $lookup: {
569
+ from: 'users',
570
+ localField: 'authorId',
571
+ foreignField: '_id',
572
+ as: 'author'
573
+ }},
574
+ { $unwind: '$author' },
575
+ { $project: {
576
+ title: 1,
577
+ content: 1,
578
+ createdAt: 1,
579
+ 'author.name': 1,
580
+ 'author.email': 1
581
+ }}
582
+ ])
583
+ .toArray();
584
+
585
+ res.json(posts);
586
+ }
587
+ ```
588
+ </details>
589
+
590
+ <details>
591
+ <summary><b>MySQL Example</b></summary>
592
+
593
+ ```js
594
+ // api/products.js
595
+ export async function get(req, res) {
596
+ // Complex SQL query with joins
597
+ const [products] = await req.db.execute(`
598
+ SELECT
599
+ p.id,
600
+ p.name,
601
+ p.price,
602
+ c.name as categoryName
603
+ FROM
604
+ products p
605
+ JOIN
606
+ categories c ON p.category_id = c.id
607
+ WHERE
608
+ p.active = ?
609
+ ORDER BY
610
+ p.created_at DESC
611
+ LIMIT 20
612
+ `, [true]);
613
+
614
+ res.json(products);
615
+ }
616
+ ```
617
+ </details>
618
+
619
+ <details>
620
+ <summary><b>PostgreSQL Example</b></summary>
621
+
622
+ ```js
623
+ // api/analytics.js
624
+ export async function get(req, res) {
625
+ // Advanced PostgreSQL features
626
+ const result = await req.db.query(`
627
+ WITH monthly_sales AS (
628
+ SELECT
629
+ date_trunc('month', order_date) as month,
630
+ SUM(total_amount) as revenue
631
+ FROM
632
+ orders
633
+ WHERE
634
+ order_date > NOW() - INTERVAL '1 year'
635
+ GROUP BY
636
+ date_trunc('month', order_date)
637
+ )
638
+ SELECT
639
+ month,
640
+ revenue,
641
+ lag(revenue) OVER (ORDER BY month) as prev_month_revenue,
642
+ round((revenue - lag(revenue) OVER (ORDER BY month)) /
643
+ lag(revenue) OVER (ORDER BY month) * 100, 2) as growth_percent
644
+ FROM
645
+ monthly_sales
646
+ ORDER BY
647
+ month
648
+ `);
649
+
650
+ res.json(result.rows);
651
+ }
652
+ ```
653
+ </details>
654
+
655
+ ### Authentication System
656
+
657
+ <details>
658
+ <summary><b>JWT Authentication</b></summary>
659
+
660
+ ```js
661
+ // api/auth/login.js
662
+ export async function post(req, res) {
663
+ const { email, password } = req.body;
664
+
665
+ // Validate input
666
+ if (!email || !password) {
667
+ return res.status(400).json({ error: 'Email and password are required' });
668
+ }
669
+
670
+ try {
671
+ // Find user by email
672
+ const user = await req.db.collection('users').findOne({ email });
673
+
674
+ // Check if user exists and password is correct
675
+ if (!user || !await req.auth.comparePasswords(password, user.password)) {
676
+ return res.status(401).json({ error: 'Invalid credentials' });
677
+ }
678
+
679
+ // Generate JWT token
680
+ const token = req.auth.generateToken({
681
+ id: user._id,
682
+ name: user.name,
683
+ email: user.email,
684
+ roles: user.roles || ['user']
685
+ });
686
+
687
+ // Return token and user info (excluding sensitive data)
688
+ res.json({
689
+ token,
690
+ user: {
691
+ id: user._id,
692
+ name: user.name,
693
+ email: user.email,
694
+ roles: user.roles || ['user']
695
+ }
696
+ });
697
+ } catch (error) {
698
+ res.status(500).json({ error: 'Authentication failed' });
699
+ }
700
+ }
701
+ ```
702
+
703
+ ```js
704
+ // api/auth/register.js
705
+ export async function post(req, res) {
706
+ const { name, email, password } = req.body;
707
+
708
+ // Validate input
709
+ if (!name || !email || !password) {
710
+ return res.status(400).json({
711
+ error: 'Name, email, and password are required'
712
+ });
713
+ }
714
+
715
+ try {
716
+ // Check if user already exists
717
+ const existingUser = await req.db.collection('users').findOne({ email });
718
+ if (existingUser) {
719
+ return res.status(409).json({ error: 'Email already in use' });
720
+ }
721
+
722
+ // Hash password
723
+ const hashedPassword = await req.auth.hashPassword(password);
724
+
725
+ // Create user
726
+ const result = await req.db.collection('users').insertOne({
727
+ name,
728
+ email,
729
+ password: hashedPassword,
730
+ roles: ['user'],
731
+ createdAt: new Date()
732
+ });
733
+
734
+ res.status(201).json({
735
+ id: result.insertedId,
736
+ name,
737
+ email,
738
+ roles: ['user']
739
+ });
740
+ } catch (error) {
741
+ res.status(500).json({ error: 'Registration failed' });
742
+ }
743
+ }
744
+ ```
745
+
746
+ ```js
747
+ // middleware/requireAuth.js - Protected route middleware
748
+ export default function requireAuth(req, res, next) {
749
+ const token = req.headers.authorization?.split(' ')[1];
750
+
751
+ if (!token) {
752
+ return res.status(401).json({ error: 'Authentication required' });
753
+ }
754
+
755
+ try {
756
+ // Verify token
757
+ const decoded = req.auth.verifyToken(token);
758
+ req.user = decoded; // Attach user to request
759
+ next();
760
+ } catch (error) {
761
+ return res.status(401).json({ error: 'Invalid or expired token' });
762
+ }
763
+ }
764
+
765
+ // api/profile.js - Protected route example
766
+ import requireAuth from '../middleware/requireAuth.js';
767
+
768
+ export const middleware = [requireAuth];
769
+
770
+ export async function get(req, res) {
771
+ // req.user is available from requireAuth middleware
772
+ const { id } = req.user;
773
+
774
+ const profile = await req.db.collection('users').findOne(
775
+ { _id: new ObjectId(id) },
776
+ { projection: { password: 0 } } // Exclude password
777
+ );
778
+
779
+ res.json(profile);
780
+ }
781
+ ```
782
+ </details>
783
+
784
+ ## 🔄 Server-Side Rendering
785
+
786
+ Frontend Hamroun provides built-in server-side rendering capabilities to improve performance and SEO:
787
+
788
+ <details>
789
+ <summary><b>Server-Side Rendering Setup</b></summary>
185
790
 
186
791
  ```jsx
187
- // Server-side
792
+ // server.js
793
+ import express from 'express';
188
794
  import { renderToString } from 'frontend-hamroun/ssr';
189
795
  import App from './App';
190
796
 
797
+ const app = express();
798
+ app.use(express.static('public'));
799
+
191
800
  app.get('*', async (req, res) => {
801
+ // Render app to string
192
802
  const html = await renderToString(<App url={req.url} />);
193
803
 
804
+ // Send complete HTML document
194
805
  res.send(`
195
806
  <!DOCTYPE html>
196
807
  <html>
197
808
  <head>
198
809
  <title>My SSR App</title>
810
+ <meta charset="UTF-8">
811
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
199
812
  <link rel="stylesheet" href="/styles.css">
200
813
  </head>
201
814
  <body>
202
815
  <div id="root">${html}</div>
203
- <script src="/client.js"></script>
816
+ <script src="/bundle.js"></script>
204
817
  </body>
205
818
  </html>
206
819
  `);
207
820
  });
208
821
 
209
- // Client-side
822
+ app.listen(3000, () => {
823
+ console.log('Server running at http://localhost:3000');
824
+ });
825
+
826
+ // client.js (for hydration)
210
827
  import { hydrate } from 'frontend-hamroun';
211
828
  import App from './App';
212
829
 
830
+ // Hydrate the app in the browser
213
831
  hydrate(<App url={window.location.pathname} />, document.getElementById('root'));
214
832
  ```
833
+ </details>
215
834
 
216
- ## Performance Features
835
+ ## 🛠️ CLI Tools
217
836
 
218
- ### Batch Updates
219
- Group multiple state updates together to prevent unnecessary re-renders:
837
+ Frontend Hamroun includes a powerful CLI for scaffolding projects, components, and API routes:
220
838
 
221
- ```jsx
222
- import { batchUpdates } from 'frontend-hamroun';
223
-
224
- function handleSubmit() {
225
- batchUpdates(() => {
226
- setSubmitting(true);
227
- setFormData({ name: '', email: '' });
228
- setErrors({});
229
- setSubmitCount(c => c + 1);
230
- });
231
- }
839
+ <details>
840
+ <summary><b>Project Creation</b></summary>
841
+
842
+ ```bash
843
+ # Create a new project with interactive prompts
844
+ npx frontend-hamroun create my-app
845
+
846
+ # Create with specific template
847
+ npx frontend-hamroun create my-app --template fullstack-app
232
848
  ```
233
849
 
234
- ## CLI Tools
850
+ Available templates:
851
+ - `basic-app`: Minimal client-side setup
852
+ - `ssr-template`: Server-side rendering with hydration
853
+ - `fullstack-app`: Complete solution with frontend, backend, auth, and DB
854
+ </details>
235
855
 
236
- Frontend Hamroun includes powerful CLI tools for development:
856
+ <details>
857
+ <summary><b>Component Generation</b></summary>
237
858
 
238
859
  ```bash
239
- # Create a new project
240
- npx frontend-hamroun create my-app
241
-
242
860
  # Generate a new component
243
861
  npx frontend-hamroun add:component Button
244
862
 
245
- # Create a new page
246
- npx frontend-hamroun add:page Home
863
+ # Generate a TypeScript component
864
+ npx frontend-hamroun add:component UserProfile --typescript
247
865
 
248
- # Generate an API route
249
- npx frontend-hamroun add:api users --methods=get,post,put,delete
866
+ # Specify path and hooks
867
+ npx frontend-hamroun add:component Sidebar --path=src/layout --hooks=useState,useEffect
250
868
  ```
869
+ </details>
251
870
 
252
- ## Project Templates
871
+ <details>
872
+ <summary><b>API Route Generation</b></summary>
253
873
 
254
- Choose from multiple project templates:
874
+ ```bash
875
+ # Generate API route
876
+ npx frontend-hamroun add:api products
877
+
878
+ # Specify HTTP methods and auth requirement
879
+ npx frontend-hamroun add:api orders --methods=get,post --auth
880
+ ```
881
+ </details>
882
+
883
+ ## 🧩 Advanced Usage
884
+
885
+ <details>
886
+ <summary><b>Custom Hooks</b></summary>
255
887
 
256
- 1. **Basic App**: Client-side SPA with essential features
257
- 2. **SSR Template**: Server-side rendering with hydration
258
- 3. **Fullstack App**: Complete solution with API, authentication, and database
888
+ ```jsx
889
+ import { useState, useEffect } from 'frontend-hamroun';
890
+
891
+ // Custom hook for fetching data
892
+ function useFetch(url, options = {}) {
893
+ const [data, setData] = useState(null);
894
+ const [loading, setLoading] = useState(true);
895
+ const [error, setError] = useState(null);
896
+
897
+ useEffect(() => {
898
+ setLoading(true);
899
+ setError(null);
900
+
901
+ fetch(url, options)
902
+ .then(response => {
903
+ if (!response.ok) {
904
+ throw new Error(`HTTP error ${response.status}`);
905
+ }
906
+ return response.json();
907
+ })
908
+ .then(json => {
909
+ setData(json);
910
+ setLoading(false);
911
+ })
912
+ .catch(err => {
913
+ setError(err.message);
914
+ setLoading(false);
915
+ });
916
+ }, [url]);
917
+
918
+ return { data, loading, error };
919
+ }
920
+
921
+ // Usage
922
+ function UserList() {
923
+ const { data, loading, error } = useFetch('/api/users');
924
+
925
+ if (loading) return <div>Loading...</div>;
926
+ if (error) return <div>Error: {error}</div>;
927
+
928
+ return (
929
+ <ul>
930
+ {data.map(user => (
931
+ <li key={user.id}>{user.name}</li>
932
+ ))}
933
+ </ul>
934
+ );
935
+ }
936
+ ```
937
+ </details>
259
938
 
260
- ## TypeScript Support
939
+ <details>
940
+ <summary><b>Performance Optimization Techniques</b></summary>
941
+
942
+ ```jsx
943
+ import { useMemo, useState, useRef, batchUpdates } from 'frontend-hamroun';
944
+
945
+ function OptimizedList({ items }) {
946
+ const [filter, setFilter] = useState('');
947
+ const [sortOrder, setSortOrder] = useState('asc');
948
+ const prevItems = useRef(items);
949
+
950
+ // Memoize expensive calculations
951
+ const processedItems = useMemo(() => {
952
+ console.log('Processing items...');
953
+ let result = [...items];
954
+
955
+ // Apply filter
956
+ if (filter) {
957
+ result = result.filter(item =>
958
+ item.name.toLowerCase().includes(filter.toLowerCase())
959
+ );
960
+ }
961
+
962
+ // Apply sorting
963
+ result.sort((a, b) => {
964
+ const comparison = a.name.localeCompare(b.name);
965
+ return sortOrder === 'asc' ? comparison : -comparison;
966
+ });
967
+
968
+ return result;
969
+ }, [items, filter, sortOrder]);
970
+
971
+ // Check for changed items with more control than dependency arrays
972
+ if (items !== prevItems.current) {
973
+ console.log('Items array reference changed');
974
+ prevItems.current = items;
975
+ }
976
+
977
+ // Batch multiple state updates together
978
+ const toggleSortAndClear = () => {
979
+ batchUpdates(() => {
980
+ setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc');
981
+ setFilter('');
982
+ });
983
+ };
984
+
985
+ return (
986
+ <div>
987
+ <div>
988
+ <input
989
+ type="text"
990
+ value={filter}
991
+ onChange={e => setFilter(e.target.value)}
992
+ placeholder="Filter items..."
993
+ />
994
+ <button onClick={toggleSortAndClear}>
995
+ Toggle Sort ({sortOrder})
996
+ </button>
997
+ </div>
998
+
999
+ {/* Using key for efficient list rendering */}
1000
+ <ul>
1001
+ {processedItems.map(item => (
1002
+ <li key={item.id}>{item.name}</li>
1003
+ ))}
1004
+ </ul>
1005
+ </div>
1006
+ );
1007
+ }
1008
+ ```
1009
+ </details>
1010
+
1011
+ ## 📝 TypeScript Support
1012
+
1013
+ Frontend Hamroun is built with TypeScript and comes with complete type definitions:
1014
+
1015
+ <details>
1016
+ <summary><b>Type-Safe Component Example</b></summary>
261
1017
 
262
1018
  ```tsx
263
- import { useState } from 'frontend-hamroun';
1019
+ import { useState, useEffect } from 'frontend-hamroun';
264
1020
 
265
- interface UserProps {
1021
+ // Define props interface
1022
+ interface UserProfileProps {
266
1023
  id: number;
267
1024
  name: string;
268
1025
  email: string;
1026
+ role: 'admin' | 'user' | 'guest';
1027
+ onStatusChange?: (id: number, active: boolean) => void;
1028
+ }
1029
+
1030
+ // Define state interface
1031
+ interface UserProfileState {
1032
+ isActive: boolean;
1033
+ isEditing: boolean;
1034
+ formData: {
1035
+ name: string;
1036
+ email: string;
1037
+ };
269
1038
  }
270
1039
 
271
- function UserProfile({ id, name, email }: UserProps) {
272
- const [isEditing, setEditing] = useState(false);
1040
+ function UserProfile({
1041
+ id,
1042
+ name,
1043
+ email,
1044
+ role,
1045
+ onStatusChange
1046
+ }: UserProfileProps) {
1047
+ // Type-safe state
1048
+ const [state, setState] = useState<UserProfileState>({
1049
+ isActive: true,
1050
+ isEditing: false,
1051
+ formData: {
1052
+ name,
1053
+ email
1054
+ }
1055
+ });
1056
+
1057
+ // Type-safe event handlers
1058
+ const toggleStatus = (): void => {
1059
+ const newStatus = !state.isActive;
1060
+ setState(prev => ({
1061
+ ...prev,
1062
+ isActive: newStatus
1063
+ }));
1064
+
1065
+ if (onStatusChange) {
1066
+ onStatusChange(id, newStatus);
1067
+ }
1068
+ };
1069
+
1070
+ // Type-safe refs
1071
+ const formRef = useRef<HTMLFormElement>(null);
273
1072
 
274
1073
  return (
275
1074
  <div className="user-profile">
276
1075
  <h2>{name}</h2>
277
- <p>{email}</p>
278
- <button onClick={() => setEditing(!isEditing)}>
279
- {isEditing ? 'Cancel' : 'Edit'}
1076
+ <p>Email: {email}</p>
1077
+ <p>Role: {role}</p>
1078
+ <p>Status: {state.isActive ? 'Active' : 'Inactive'}</p>
1079
+
1080
+ <button onClick={toggleStatus}>
1081
+ {state.isActive ? 'Deactivate' : 'Activate'}
280
1082
  </button>
1083
+
1084
+ {state.isEditing ? (
1085
+ <form ref={formRef}>
1086
+ {/* Form fields */}
1087
+ </form>
1088
+ ) : (
1089
+ <button onClick={() => setState(prev => ({
1090
+ ...prev,
1091
+ isEditing: true
1092
+ }))}>
1093
+ Edit
1094
+ </button>
1095
+ )}
281
1096
  </div>
282
1097
  );
283
1098
  }
1099
+
1100
+ // Usage with type checking
1101
+ const App = () => (
1102
+ <div>
1103
+ <UserProfile
1104
+ id={1}
1105
+ name="John Doe"
1106
+ email="john@example.com"
1107
+ role="admin"
1108
+ onStatusChange={(id, active) => {
1109
+ console.log(`User ${id} status changed to ${active}`);
1110
+ }}
1111
+ />
1112
+
1113
+ {/* This would cause TypeScript errors */}
1114
+ {/*
1115
+ <UserProfile
1116
+ id="1" // Error: string is not assignable to number
1117
+ name="Jane Doe"
1118
+ role="manager" // Error: 'manager' is not assignable
1119
+ />
1120
+ */}
1121
+ </div>
1122
+ );
284
1123
  ```
1124
+ </details>
285
1125
 
286
- ## Browser Compatibility
1126
+ <details>
1127
+ <summary><b>Type-Safe API Routes</b></summary>
287
1128
 
288
- Frontend Hamroun works in all modern browsers (Chrome, Firefox, Safari, Edge) and in IE11 with appropriate polyfills.
1129
+ ```ts
1130
+ // types.ts
1131
+ export interface User {
1132
+ id?: string;
1133
+ name: string;
1134
+ email: string;
1135
+ password?: string;
1136
+ role: 'admin' | 'user';
1137
+ createdAt?: Date;
1138
+ updatedAt?: Date;
1139
+ }
289
1140
 
290
- ## Documentation
1141
+ // api/users/[id].ts
1142
+ import type { User } from '../../types';
291
1143
 
292
- For complete documentation, visit our [GitHub repository](https://github.com/hamroun/frontend-hamroun).
1144
+ export async function get(req, res) {
1145
+ const { id } = req.params;
1146
+
1147
+ try {
1148
+ const user = await req.db.collection('users').findOne({
1149
+ _id: new ObjectId(id)
1150
+ }) as User;
1151
+
1152
+ if (!user) {
1153
+ return res.status(404).json({ error: 'User not found' });
1154
+ }
1155
+
1156
+ // Omit sensitive data
1157
+ const { password, ...safeUser } = user;
1158
+
1159
+ res.json(safeUser);
1160
+ } catch (error) {
1161
+ res.status(500).json({ error: 'Server error' });
1162
+ }
1163
+ }
293
1164
 
294
- ## License
1165
+ export async function put(req, res) {
1166
+ const { id } = req.params;
1167
+ const { name, email, role } = req.body as Partial<User>;
1168
+
1169
+ // Validate that role is a valid enum value
1170
+ if (role && !['admin', 'user'].includes(role)) {
1171
+ return res.status(400).json({ error: 'Invalid role' });
1172
+ }
1173
+
1174
+ try {
1175
+ await req.db.collection('users').updateOne(
1176
+ { _id: new ObjectId(id) },
1177
+ {
1178
+ $set: {
1179
+ ...(name && { name }),
1180
+ ...(email && { email }),
1181
+ ...(role && { role }),
1182
+ updatedAt: new Date()
1183
+ }
1184
+ }
1185
+ );
1186
+
1187
+ res.json({ success: true });
1188
+ } catch (error) {
1189
+ res.status(500).json({ error: 'Server error' });
1190
+ }
1191
+ }
1192
+ ```
1193
+ </details>
1194
+
1195
+ ## 🌐 Browser Compatibility
1196
+
1197
+ Frontend Hamroun supports all modern browsers out of the box:
1198
+
1199
+ - Chrome (latest 2 versions)
1200
+ - Firefox (latest 2 versions)
1201
+ - Safari (latest 2 versions)
1202
+ - Edge (latest 2 versions)
1203
+
1204
+ For older browsers like IE11, you'll need:
1205
+ - Appropriate polyfills
1206
+ - Transpilation to ES5
1207
+ - CSS compatibility work
1208
+
1209
+ ## ❓ Frequently Asked Questions
1210
+
1211
+ <details>
1212
+ <summary><b>How does Frontend Hamroun compare to React?</b></summary>
1213
+
1214
+ Frontend Hamroun offers a React-like API but with additional built-in features:
1215
+ - Smaller bundle size (~5KB vs. 42KB+)
1216
+ - Integrated backend capabilities
1217
+ - Built-in server-side rendering
1218
+ - Database integrations
1219
+ - Authentication system
1220
+
1221
+ For React developers, the learning curve is minimal.
1222
+ </details>
1223
+
1224
+ <details>
1225
+ <summary><b>Can I use it with existing React components?</b></summary>
1226
+
1227
+ While Frontend Hamroun's API is similar to React's, they have different internal implementations. You can:
1228
+ - Port React components to Frontend Hamroun with minimal changes
1229
+ - Use React components in dedicated sections with a compatibility layer
1230
+ - Share non-UI code and logic between both frameworks
1231
+ </details>
1232
+
1233
+ <details>
1234
+ <summary><b>Does it support static site generation (SSG)?</b></summary>
1235
+
1236
+ Yes, Frontend Hamroun provides tools for static site generation. Use the `renderToString` API to pre-render pages at build time.
1237
+ </details>
1238
+
1239
+ ## 📄 License
295
1240
 
296
1241
  MIT © Hamroun
1242
+
1243
+ ---
1244
+
1245
+ <p align="center">
1246
+ <a href="#-table-of-contents">⬆️ Back to top</a>
1247
+ </p>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "frontend-hamroun",
3
- "version": "1.2.63",
3
+ "version": "1.2.65",
4
4
  "description": "A lightweight full-stack JavaScript framework",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",