frontend-hamroun 1.2.79 → 1.2.82
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 +506 -145
- 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 +299 -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/dev.js +27 -0
- package/templates/basic-app/esbuild.config.js +28 -0
- package/templates/basic-app/index.html +1 -1
- package/templates/basic-app/package.json +29 -28
- package/templates/basic-app/server.js +24 -0
- package/templates/basic-app/src/App.jsx +16 -0
- package/templates/basic-app/src/App.tsx +26 -0
- package/templates/basic-app/src/client.jsx +5 -0
- package/templates/basic-app/src/client.tsx +11 -0
- package/templates/basic-app/src/components/Counter.jsx +13 -0
- package/templates/basic-app/src/components/Counter.tsx +18 -0
- package/templates/basic-app/src/jsx-shim.js +3 -0
- package/templates/basic-app/src/jsx-shim.ts +11 -0
- package/templates/basic-app/src/main.jsx +98 -0
- package/templates/basic-app/src/main.tsx +0 -1
- package/templates/basic-app/src/server.js +47 -0
- package/templates/basic-app/src/server.ts +52 -0
- package/templates/basic-app/tsconfig.server.json +11 -0
- package/templates/complete-app/lib/frontend-hamroun.js +182 -0
- package/templates/complete-app/package.json +2 -1
- package/templates/complete-app/pages/about.jsx +0 -0
- package/templates/complete-app/pages/index.jsx +0 -0
- package/templates/complete-app/pages/wasm-demo.jsx +0 -0
- package/templates/complete-app/public/client.js +58 -49
- package/templates/complete-app/public/index.html +88 -17
- package/templates/complete-app/public/styles.css +30 -533
- package/templates/complete-app/server.js +31 -222
- package/templates/complete-app/wasm/build.bat +0 -0
- package/templates/complete-app/wasm/build.sh +0 -0
- package/templates/complete-app/wasm/example.go +0 -0
- package/templates/fullstack-app/build/main.js +130 -101
- package/templates/fullstack-app/build/main.js.map +4 -4
- package/templates/fullstack-app/package-lock.json +1773 -566
- package/templates/ssr-template/esbuild.config.js +33 -0
- package/templates/ssr-template/jsx-shim.js +1 -0
- package/templates/ssr-template/package.json +22 -16
- package/templates/ssr-template/src/App.tsx +12 -52
- package/templates/ssr-template/src/client.tsx +3 -17
- package/templates/ssr-template/src/server.ts +21 -204
- package/templates/ssr-template/tsconfig.json +10 -13
- package/templates/ssr-template/tsconfig.server.json +6 -14
- package/templates/wasm/build-wasm.js +228 -0
- package/templates/wasm/esbuild.config.js +63 -0
- package/templates/wasm/go/main.go +256 -0
- package/templates/wasm/go/wasm_exec.js +0 -0
- package/templates/wasm/index.html +97 -0
- package/templates/wasm/jsx-shim.js +9 -0
- package/templates/{go-wasm-app → wasm}/package-lock.json +5307 -3732
- package/templates/wasm/package.json +42 -0
- package/templates/wasm/public/example.wasm +0 -0
- package/templates/wasm/src/App.tsx +564 -0
- package/templates/wasm/src/client.tsx +220 -0
- package/templates/wasm/src/index.tsx +21 -0
- package/templates/wasm/src/server.ts +145 -0
- package/templates/wasm/tsconfig.json +21 -0
- package/templates/wasm/tsconfig.node.json +13 -0
- package/templates/wasm/tsconfig.server.json +23 -0
- package/templates/wasm/vite.config.ts +56 -0
- package/templates/wasm/wasm-loader.js +103 -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.aux +0 -27
- package/templates/basic-app/docs/rapport_pfe.log +0 -399
- package/templates/basic-app/docs/rapport_pfe.out +0 -10
- package/templates/basic-app/docs/rapport_pfe.pdf +0 -0
- package/templates/basic-app/docs/rapport_pfe.tex +0 -68
- package/templates/basic-app/docs/rapport_pfe.toc +0 -14
- package/templates/complete-app/package-lock.json +0 -2536
- package/templates/go-wasm-app/README.md +0 -38
- package/templates/go-wasm-app/babel.config.js +0 -21
- package/templates/go-wasm-app/build-client.js +0 -49
- package/templates/go-wasm-app/build-wasm.js +0 -237
- 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.json +0 -32
- package/templates/go-wasm-app/public/index.html +0 -128
- package/templates/go-wasm-app/public/styles.css +0 -197
- package/templates/go-wasm-app/public/wasm/example.wasm +0 -0
- package/templates/go-wasm-app/public/wasm/wasm_exec_node.js +0 -39
- package/templates/go-wasm-app/server.js +0 -70
- package/templates/go-wasm-app/src/App.jsx +0 -38
- package/templates/go-wasm-app/src/app.js +0 -173
- package/templates/go-wasm-app/src/client.js +0 -57
- package/templates/go-wasm-app/src/components/Footer.jsx +0 -13
- package/templates/go-wasm-app/src/components/Header.jsx +0 -19
- package/templates/go-wasm-app/src/components/WasmDemo.jsx +0 -120
- package/templates/go-wasm-app/src/main.jsx +0 -12
- package/templates/go-wasm-app/src/wasm/example.go +0 -75
- package/templates/go-wasm-app/tsconfig.server.json +0 -18
- package/templates/go-wasm-app/vite.config.js +0 -45
- package/templates/ssr-template/client.js +0 -58
- package/templates/ssr-template/package-lock.json +0 -2478
- package/templates/ssr-template/public/index.html +0 -47
- package/templates/ssr-template/readme.md +0 -188
- package/templates/ssr-template/server.js +0 -369
- package/templates/ssr-template/server.ts +0 -275
- package/templates/ssr-template/src/client.ts +0 -61
- package/templates/ssr-template/src/pages/index.tsx +0 -51
- package/templates/ssr-template/vite.config.js +0 -57
- /package/{dist/Counter.d.ts → templates/complete-app/api/hello.js} +0 -0
- /package/templates/{go-wasm-app/public/wasm → wasm/public}/wasm_exec.js +0 -0
@@ -1,47 +0,0 @@
|
|
1
|
-
<!DOCTYPE html>
|
2
|
-
<html lang="en">
|
3
|
-
<head>
|
4
|
-
<meta charset="UTF-8">
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6
|
-
<title>Static Fallback</title>
|
7
|
-
<!-- Import Tailwind-like styles for quick styling -->
|
8
|
-
<link href="https://cdn.jsdelivr.net/npm/daisyui@3.7.4/dist/full.css" rel="stylesheet" type="text/css" />
|
9
|
-
<script src="https://cdn.tailwindcss.com"></script>
|
10
|
-
<!-- Client-side script for hydration -->
|
11
|
-
<script type="module" src="/src/client.tsx"></script>
|
12
|
-
<style>
|
13
|
-
body {
|
14
|
-
font-family: sans-serif;
|
15
|
-
max-width: 800px;
|
16
|
-
margin: 0 auto;
|
17
|
-
padding: 2rem;
|
18
|
-
line-height: 1.6;
|
19
|
-
}
|
20
|
-
.warning {
|
21
|
-
background-color: #fff3cd;
|
22
|
-
border: 1px solid #ffecb5;
|
23
|
-
color: #856404;
|
24
|
-
padding: 1rem;
|
25
|
-
border-radius: 4px;
|
26
|
-
margin-bottom: 1rem;
|
27
|
-
}
|
28
|
-
code {
|
29
|
-
background-color: #f5f5f5;
|
30
|
-
padding: 0.2rem 0.4rem;
|
31
|
-
border-radius: 3px;
|
32
|
-
}
|
33
|
-
</style>
|
34
|
-
</head>
|
35
|
-
<body>
|
36
|
-
<!-- App will be rendered here by SSR -->
|
37
|
-
<div id="app">
|
38
|
-
<div class="warning">
|
39
|
-
<h2>STATIC INDEX.HTML FILE</h2>
|
40
|
-
<p>You should NOT be seeing this page if the server is running correctly.</p>
|
41
|
-
<p>This is a static file that should only be used as a fallback when the server is not running.</p>
|
42
|
-
<p>If you're seeing this while running <code>npm run dev</code>, there's likely an issue with the Express route handling.</p>
|
43
|
-
<p>Try clearing your browser cache and refreshing the page.</p>
|
44
|
-
</div>
|
45
|
-
</div>
|
46
|
-
</body>
|
47
|
-
</html>
|
@@ -1,188 +0,0 @@
|
|
1
|
-
# Frontend Hamroun SSR Template
|
2
|
-
|
3
|
-
This is a comprehensive server-side rendering (SSR) example using Frontend Hamroun.
|
4
|
-
|
5
|
-
## Getting Started
|
6
|
-
|
7
|
-
1. Install dependencies:
|
8
|
-
```
|
9
|
-
npm install
|
10
|
-
```
|
11
|
-
|
12
|
-
2. Start the server:
|
13
|
-
```
|
14
|
-
npm start
|
15
|
-
```
|
16
|
-
|
17
|
-
3. Open your browser at http://localhost:3000
|
18
|
-
|
19
|
-
## Core Features
|
20
|
-
|
21
|
-
This template demonstrates:
|
22
|
-
|
23
|
-
### Server-Side Rendering
|
24
|
-
- Pre-rendering of components on the server
|
25
|
-
- Hydration of server-rendered content on the client
|
26
|
-
- Data fetching during server rendering
|
27
|
-
- AI-powered meta tag generation
|
28
|
-
|
29
|
-
### Automatic File-Based Routing
|
30
|
-
- Pages are automatically rendered based on their file path in the `pages` directory
|
31
|
-
- For example:
|
32
|
-
- `/pages/index.js` → `/` route
|
33
|
-
- `/pages/about.js` → `/about` route
|
34
|
-
- `/pages/blog/index.js` → `/blog` route
|
35
|
-
- `/pages/users/[id].js` → `/users/:id` dynamic route
|
36
|
-
|
37
|
-
### API Integration
|
38
|
-
- RESTful API endpoints
|
39
|
-
- Dynamic API routing
|
40
|
-
- API middleware for validation and security
|
41
|
-
- File-based API structure
|
42
|
-
|
43
|
-
### Database Integration
|
44
|
-
- MongoDB, MySQL, and PostgreSQL support
|
45
|
-
- ORM-like query interface
|
46
|
-
- Connection pooling
|
47
|
-
- Transaction support
|
48
|
-
|
49
|
-
### Authentication & Authorization
|
50
|
-
- JWT-based authentication
|
51
|
-
- Role-based access control
|
52
|
-
- Password hashing and validation
|
53
|
-
- Token refresh mechanism
|
54
|
-
|
55
|
-
### Performance Optimization
|
56
|
-
- Caching strategies
|
57
|
-
- Response compression
|
58
|
-
- Static asset optimization
|
59
|
-
- Efficient metadata handling
|
60
|
-
|
61
|
-
## Implementation Examples
|
62
|
-
|
63
|
-
### Creating Components
|
64
|
-
|
65
|
-
Use the `jsx` or `createElement` function from 'frontend-hamroun':
|
66
|
-
|
67
|
-
```jsx
|
68
|
-
import { jsx } from 'frontend-hamroun';
|
69
|
-
|
70
|
-
export default function MyComponent(props) {
|
71
|
-
return jsx('div', { className: "container" }, [
|
72
|
-
jsx('h1', {}, "Hello World"),
|
73
|
-
jsx('p', {}, `Props value: ${props.value}`)
|
74
|
-
]);
|
75
|
-
}
|
76
|
-
```
|
77
|
-
|
78
|
-
### Creating API Routes
|
79
|
-
|
80
|
-
Create files in the `api` directory following this pattern:
|
81
|
-
|
82
|
-
```typescript
|
83
|
-
// api/users/index.ts
|
84
|
-
import { Request, Response } from 'express';
|
85
|
-
|
86
|
-
export const get = (req: Request, res: Response) => {
|
87
|
-
res.json({ users: [...] });
|
88
|
-
};
|
89
|
-
|
90
|
-
export const post = (req: Request, res: Response) => {
|
91
|
-
// Create user
|
92
|
-
res.status(201).json({ success: true });
|
93
|
-
};
|
94
|
-
```
|
95
|
-
|
96
|
-
### Database Usage
|
97
|
-
|
98
|
-
```typescript
|
99
|
-
import { Server } from 'frontend-hamroun/server';
|
100
|
-
|
101
|
-
const server = new Server({
|
102
|
-
db: {
|
103
|
-
url: process.env.DATABASE_URL,
|
104
|
-
type: 'mongodb' // or 'mysql', 'postgres'
|
105
|
-
}
|
106
|
-
});
|
107
|
-
|
108
|
-
// Get typed database instance
|
109
|
-
const db = server.getDatabase();
|
110
|
-
const users = await db.query('SELECT * FROM users'); // For SQL
|
111
|
-
const docs = await db.getMongoDb().collection('users').find().toArray(); // For MongoDB
|
112
|
-
```
|
113
|
-
|
114
|
-
### Authentication
|
115
|
-
|
116
|
-
```typescript
|
117
|
-
import { AuthService } from 'frontend-hamroun/server';
|
118
|
-
|
119
|
-
const auth = new AuthService({
|
120
|
-
secret: process.env.JWT_SECRET,
|
121
|
-
expiresIn: '24h'
|
122
|
-
});
|
123
|
-
|
124
|
-
// Protect routes
|
125
|
-
app.get('/api/protected', auth.requireAuth(), (req, res) => {
|
126
|
-
res.json({ message: "Authenticated!" });
|
127
|
-
});
|
128
|
-
|
129
|
-
// Create user & login
|
130
|
-
const hashedPassword = await auth.hashPassword(password);
|
131
|
-
const token = auth.generateToken(user);
|
132
|
-
```
|
133
|
-
|
134
|
-
### Advanced Middleware
|
135
|
-
|
136
|
-
```typescript
|
137
|
-
import { rateLimit, requestLogger, errorHandler } from 'frontend-hamroun/server';
|
138
|
-
|
139
|
-
app.use(requestLogger);
|
140
|
-
app.use('/api', rateLimit({ windowMs: 15 * 60 * 1000, max: 100 }));
|
141
|
-
app.use(errorHandler);
|
142
|
-
```
|
143
|
-
|
144
|
-
## File-Based Routing Examples
|
145
|
-
|
146
|
-
### Static Routes
|
147
|
-
Create files in the `pages` directory:
|
148
|
-
|
149
|
-
```jsx
|
150
|
-
// pages/index.js - Maps to "/"
|
151
|
-
export default function HomePage() {
|
152
|
-
return <h1>Home Page</h1>;
|
153
|
-
}
|
154
|
-
|
155
|
-
// pages/about.js - Maps to "/about"
|
156
|
-
export default function AboutPage() {
|
157
|
-
return <h1>About Us</h1>;
|
158
|
-
}
|
159
|
-
|
160
|
-
// pages/contact/index.js - Maps to "/contact"
|
161
|
-
export default function ContactPage() {
|
162
|
-
return <h1>Contact Us</h1>;
|
163
|
-
}
|
164
|
-
```
|
165
|
-
|
166
|
-
### Dynamic Routes
|
167
|
-
Use brackets in filenames to define dynamic parameters:
|
168
|
-
|
169
|
-
```jsx
|
170
|
-
// pages/users/[id].js - Maps to "/users/:id"
|
171
|
-
export default function UserPage({ params }) {
|
172
|
-
return <h1>User Profile: {params.id}</h1>;
|
173
|
-
}
|
174
|
-
|
175
|
-
// pages/blog/[category]/[slug].js - Maps to "/blog/:category/:slug"
|
176
|
-
export default function BlogPost({ params }) {
|
177
|
-
return (
|
178
|
-
<div>
|
179
|
-
<h1>Blog Post: {params.slug}</h1>
|
180
|
-
<p>Category: {params.category}</p>
|
181
|
-
</div>
|
182
|
-
);
|
183
|
-
}
|
184
|
-
```
|
185
|
-
|
186
|
-
## Next Steps
|
187
|
-
|
188
|
-
Explore the full API documentation for more advanced features and customization options.
|
@@ -1,369 +0,0 @@
|
|
1
|
-
import express from 'express';
|
2
|
-
import path from 'path';
|
3
|
-
import { fileURLToPath } from 'url';
|
4
|
-
import fetch from 'node-fetch';
|
5
|
-
import dotenv from 'dotenv';
|
6
|
-
import fs from 'fs';
|
7
|
-
import { renderToString } from 'frontend-hamroun/ssr';
|
8
|
-
|
9
|
-
// Load environment variables
|
10
|
-
dotenv.config();
|
11
|
-
|
12
|
-
// Get __dirname equivalent in ESM
|
13
|
-
const __filename = fileURLToPath(import.meta.url);
|
14
|
-
const __dirname = path.dirname(__filename);
|
15
|
-
|
16
|
-
// Initialize express app
|
17
|
-
const app = express();
|
18
|
-
const PORT = process.env.PORT || 3000;
|
19
|
-
|
20
|
-
// Simple API endpoint
|
21
|
-
app.get('/api/hello', (req, res) => {
|
22
|
-
res.json({
|
23
|
-
message: 'Hello from Server-Side API',
|
24
|
-
time: new Date().toISOString()
|
25
|
-
});
|
26
|
-
});
|
27
|
-
|
28
|
-
// Function to generate meta tags locally without requiring OpenAI
|
29
|
-
function generateMetaTagsLocally(pageContent) {
|
30
|
-
// Simple keyword extraction - get common meaningful words
|
31
|
-
const keywordExtraction = (text) => {
|
32
|
-
// Remove common words and extract potential keywords
|
33
|
-
const commonWords = ['a', 'an', 'the', 'and', 'or', 'but', 'is', 'are', 'was', 'were',
|
34
|
-
'has', 'have', 'had', 'be', 'been', 'being', 'to', 'of', 'for', 'with', 'about', 'at'];
|
35
|
-
|
36
|
-
const words = text.toLowerCase()
|
37
|
-
.replace(/[^\w\s]/g, '') // Remove punctuation
|
38
|
-
.split(/\s+/) // Split by whitespace
|
39
|
-
.filter(word => word.length > 3 && !commonWords.includes(word)); // Filter short and common words
|
40
|
-
|
41
|
-
// Count word frequency
|
42
|
-
const wordCount = {};
|
43
|
-
words.forEach(word => {
|
44
|
-
wordCount[word] = (wordCount[word] || 0) + 1;
|
45
|
-
});
|
46
|
-
|
47
|
-
// Sort by frequency and get top keywords
|
48
|
-
return Object.entries(wordCount)
|
49
|
-
.sort((a, b) => b[1] - a[1])
|
50
|
-
.slice(0, 5)
|
51
|
-
.map(entry => entry[0])
|
52
|
-
.join(', ');
|
53
|
-
};
|
54
|
-
|
55
|
-
// Extract main topic (first heading or first sentence)
|
56
|
-
const getMainTopic = (text) => {
|
57
|
-
const headingMatch = text.match(/<h1[^>]*>(.*?)<\/h1>/i) ||
|
58
|
-
text.match(/<h2[^>]*>(.*?)<\/h2>/i);
|
59
|
-
|
60
|
-
if (headingMatch) {
|
61
|
-
return headingMatch[1].trim();
|
62
|
-
}
|
63
|
-
|
64
|
-
// If no heading, use first sentence
|
65
|
-
const firstSentence = text.split(/[.!?]/).filter(s => s.trim().length > 0)[0];
|
66
|
-
return firstSentence ? firstSentence.trim() : "Frontend Hamroun SSR Page";
|
67
|
-
};
|
68
|
-
|
69
|
-
// Generate description (first few sentences, truncated)
|
70
|
-
const getDescription = (text) => {
|
71
|
-
const plainText = text.replace(/<[^>]*>/g, ' ').replace(/\s+/g, ' ').trim();
|
72
|
-
const sentences = plainText.split(/[.!?]/).filter(s => s.trim().length > 0);
|
73
|
-
const description = sentences.slice(0, 2).join('. ');
|
74
|
-
|
75
|
-
return description.length > 160
|
76
|
-
? description.substring(0, 157) + '...'
|
77
|
-
: description;
|
78
|
-
};
|
79
|
-
|
80
|
-
// Generate the meta tags
|
81
|
-
const title = getMainTopic(pageContent);
|
82
|
-
const description = getDescription(pageContent);
|
83
|
-
const keywords = keywordExtraction(pageContent);
|
84
|
-
|
85
|
-
return {
|
86
|
-
title: title || 'Frontend Hamroun SSR App',
|
87
|
-
description: description || 'A server-side rendered application using Frontend Hamroun framework',
|
88
|
-
keywords: keywords || 'ssr, javascript, frontend, hamroun, web development'
|
89
|
-
};
|
90
|
-
}
|
91
|
-
|
92
|
-
// Function to generate meta tags - using Gemini with local fallback
|
93
|
-
async function generateMetaTags(pageContent) {
|
94
|
-
// Remove forcing local generation
|
95
|
-
process.env.USE_LOCAL_GENERATION = 'false';
|
96
|
-
|
97
|
-
// Check if local generation is forced
|
98
|
-
if (process.env.USE_LOCAL_GENERATION === 'true') {
|
99
|
-
console.log('Using local meta tag generation (forced by config)');
|
100
|
-
return generateMetaTagsLocally(pageContent);
|
101
|
-
}
|
102
|
-
|
103
|
-
try {
|
104
|
-
console.log('Attempting to generate meta tags with Gemini AI...');
|
105
|
-
return await generateMetaTagsWithGemini(pageContent);
|
106
|
-
} catch (error) {
|
107
|
-
console.error('Error generating meta tags with Gemini, falling back to local generation:', error);
|
108
|
-
return generateMetaTagsLocally(pageContent);
|
109
|
-
}
|
110
|
-
}
|
111
|
-
|
112
|
-
// Function to generate meta tags using Google's Gemini API
|
113
|
-
async function generateMetaTagsWithGemini(pageContent) {
|
114
|
-
if (!process.env.GEMINI_API_KEY) {
|
115
|
-
console.log('Gemini API key not found. Using local meta tag generation.');
|
116
|
-
return generateMetaTagsLocally(pageContent);
|
117
|
-
}
|
118
|
-
|
119
|
-
try {
|
120
|
-
console.log('Connecting to Gemini AI...');
|
121
|
-
// Use the correct model as indicated in the working curl example
|
122
|
-
const endpoints = [
|
123
|
-
'https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent'
|
124
|
-
];
|
125
|
-
|
126
|
-
let response = null;
|
127
|
-
let responseData = null;
|
128
|
-
|
129
|
-
// Try the endpoint
|
130
|
-
for (const url of endpoints) {
|
131
|
-
try {
|
132
|
-
console.log(`Trying Gemini endpoint: ${url}`);
|
133
|
-
|
134
|
-
const requestBody = {
|
135
|
-
contents: [{
|
136
|
-
parts: [{
|
137
|
-
text: `Generate SEO-friendly meta tags as JSON with the following format:
|
138
|
-
{
|
139
|
-
"title": "A concise and engaging title",
|
140
|
-
"description": "A compelling description under 160 characters",
|
141
|
-
"keywords": "keyword1, keyword2, keyword3, keyword4, keyword5"
|
142
|
-
}
|
143
|
-
|
144
|
-
The meta tags should be based on this content: ${pageContent}`
|
145
|
-
}]
|
146
|
-
}]
|
147
|
-
};
|
148
|
-
|
149
|
-
// Log the exact request for debugging
|
150
|
-
console.log('Sending request to Gemini:', JSON.stringify(requestBody, null, 2).substring(0, 150) + '...');
|
151
|
-
|
152
|
-
response = await fetch(`${url}?key=${process.env.GEMINI_API_KEY}`, {
|
153
|
-
method: 'POST',
|
154
|
-
headers: {
|
155
|
-
'Content-Type': 'application/json'
|
156
|
-
},
|
157
|
-
body: JSON.stringify(requestBody)
|
158
|
-
});
|
159
|
-
|
160
|
-
// Get the response data
|
161
|
-
responseData = await response.text();
|
162
|
-
|
163
|
-
// Try to parse it as JSON
|
164
|
-
try {
|
165
|
-
responseData = JSON.parse(responseData);
|
166
|
-
|
167
|
-
if (response.ok) {
|
168
|
-
console.log('Successfully received response from Gemini');
|
169
|
-
break;
|
170
|
-
} else {
|
171
|
-
console.error('Gemini API error response:', responseData);
|
172
|
-
}
|
173
|
-
} catch (parseError) {
|
174
|
-
console.error('Failed to parse Gemini response as JSON:', responseData.substring(0, 150));
|
175
|
-
throw parseError;
|
176
|
-
}
|
177
|
-
} catch (err) {
|
178
|
-
console.log(`Endpoint ${url} failed:`, err.message);
|
179
|
-
}
|
180
|
-
}
|
181
|
-
|
182
|
-
if (!response || !response.ok) {
|
183
|
-
throw new Error('Failed to get valid response from Gemini API');
|
184
|
-
}
|
185
|
-
|
186
|
-
// Extract the text response - using the correct response format for gemini-2.0-flash
|
187
|
-
const textResponse = responseData.candidates[0].content.parts[0].text;
|
188
|
-
|
189
|
-
// Extract the JSON object from the text response
|
190
|
-
const jsonMatch = textResponse.match(/\{[\s\S]*\}/);
|
191
|
-
if (!jsonMatch) {
|
192
|
-
console.error('Could not find JSON in response:', textResponse);
|
193
|
-
throw new Error('Could not parse JSON from Gemini response');
|
194
|
-
}
|
195
|
-
|
196
|
-
try {
|
197
|
-
const metaTagsJson = jsonMatch[0];
|
198
|
-
const metaTags = JSON.parse(metaTagsJson);
|
199
|
-
console.log('Generated meta tags using Gemini:', metaTags);
|
200
|
-
|
201
|
-
return metaTags;
|
202
|
-
} catch (jsonError) {
|
203
|
-
console.error('Failed to parse extracted JSON:', jsonMatch[0]);
|
204
|
-
throw jsonError;
|
205
|
-
}
|
206
|
-
} catch (error) {
|
207
|
-
console.error('Error generating meta tags with Gemini:', error);
|
208
|
-
throw error;
|
209
|
-
}
|
210
|
-
}
|
211
|
-
|
212
|
-
// Basic server-side rendering implementation
|
213
|
-
app.get('/', async (req, res) => {
|
214
|
-
console.log('Handling root route for SSR');
|
215
|
-
try {
|
216
|
-
// Create a simple virtual DOM tree
|
217
|
-
const vnode = {
|
218
|
-
type: 'div',
|
219
|
-
props: {
|
220
|
-
id: 'app',
|
221
|
-
children: [
|
222
|
-
{
|
223
|
-
type: 'h1',
|
224
|
-
props: {
|
225
|
-
children: 'Hello from Server-Side Rendering!'
|
226
|
-
}
|
227
|
-
},
|
228
|
-
{
|
229
|
-
type: 'p',
|
230
|
-
props: {
|
231
|
-
children: `This page was rendered at ${new Date().toISOString()}`
|
232
|
-
}
|
233
|
-
},
|
234
|
-
{
|
235
|
-
type: 'button',
|
236
|
-
props: {
|
237
|
-
id: 'counter-btn',
|
238
|
-
className: 'btn',
|
239
|
-
children: 'Click me (0)'
|
240
|
-
}
|
241
|
-
}
|
242
|
-
]
|
243
|
-
}
|
244
|
-
};
|
245
|
-
|
246
|
-
// Generate content for meta tag creation
|
247
|
-
const contentForMetaTags = 'Server-side rendered page using Frontend Hamroun framework. ' +
|
248
|
-
'This demonstrates SSR capabilities with dynamic content generation and client-side hydration.';
|
249
|
-
|
250
|
-
console.log('Fetching meta tags for the page...');
|
251
|
-
const metaTags = await generateMetaTags(contentForMetaTags);
|
252
|
-
|
253
|
-
// Generate HTML from our virtual node directly using imported renderToString
|
254
|
-
const content = renderToString(vnode);
|
255
|
-
|
256
|
-
// Send complete HTML document with explicit content type
|
257
|
-
res.setHeader('Content-Type', 'text/html');
|
258
|
-
res.send(`
|
259
|
-
<!DOCTYPE html>
|
260
|
-
<html>
|
261
|
-
<head>
|
262
|
-
<meta charset="UTF-8">
|
263
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
264
|
-
|
265
|
-
<!-- AI Generated Meta Tags -->
|
266
|
-
<title>${metaTags.title}</title>
|
267
|
-
<meta name="description" content="${metaTags.description}">
|
268
|
-
<meta name="keywords" content="${metaTags.keywords}">
|
269
|
-
|
270
|
-
<!-- Open Graph tags -->
|
271
|
-
<meta property="og:title" content="${metaTags.title}">
|
272
|
-
<meta property="og:description" content="${metaTags.description}">
|
273
|
-
<meta property="og:type" content="website">
|
274
|
-
<meta property="og:url" content="${req.protocol}://${req.get('host')}${req.originalUrl}">
|
275
|
-
|
276
|
-
<!-- Twitter Card tags -->
|
277
|
-
<meta name="twitter:card" content="summary_large_image">
|
278
|
-
<meta name="twitter:title" content="${metaTags.title}">
|
279
|
-
<meta name="twitter:description" content="${metaTags.description}">
|
280
|
-
|
281
|
-
<style>
|
282
|
-
body {
|
283
|
-
font-family: sans-serif;
|
284
|
-
max-width: 800px;
|
285
|
-
margin: 0 auto;
|
286
|
-
padding: 2rem;
|
287
|
-
}
|
288
|
-
.btn {
|
289
|
-
background-color: #4CAF50;
|
290
|
-
border: none;
|
291
|
-
color: white;
|
292
|
-
padding: 10px 20px;
|
293
|
-
cursor: pointer;
|
294
|
-
border-radius: 4px;
|
295
|
-
margin-top: 1rem;
|
296
|
-
}
|
297
|
-
</style>
|
298
|
-
<script>
|
299
|
-
// Simple client-side interactivity
|
300
|
-
document.addEventListener('DOMContentLoaded', () => {
|
301
|
-
const btn = document.getElementById('counter-btn');
|
302
|
-
if (btn) {
|
303
|
-
let count = 0;
|
304
|
-
btn.addEventListener('click', () => {
|
305
|
-
count++;
|
306
|
-
btn.textContent = \`Click me (\${count})\`;
|
307
|
-
});
|
308
|
-
console.log('Button click handler attached');
|
309
|
-
}
|
310
|
-
});
|
311
|
-
</script>
|
312
|
-
</head>
|
313
|
-
<body>${content || '<div>Error: No content generated</div>'}</body>
|
314
|
-
</html>
|
315
|
-
`);
|
316
|
-
} catch (error) {
|
317
|
-
console.error('SSR Error:', error);
|
318
|
-
|
319
|
-
// Fallback HTML with error details
|
320
|
-
res.status(500).send(`
|
321
|
-
<!DOCTYPE html>
|
322
|
-
<html>
|
323
|
-
<head>
|
324
|
-
<title>SSR Error</title>
|
325
|
-
<style>
|
326
|
-
body { font-family: sans-serif; padding: 2rem; }
|
327
|
-
pre { background: #f5f5f5; padding: 1rem; overflow: auto; }
|
328
|
-
</style>
|
329
|
-
</head>
|
330
|
-
<body>
|
331
|
-
<h1>Server-Side Rendering Error</h1>
|
332
|
-
<p>There was a problem rendering the page.</p>
|
333
|
-
<pre>${error.stack}</pre>
|
334
|
-
<p>Try refreshing the page or contact the administrator if the problem persists.</p>
|
335
|
-
</body>
|
336
|
-
</html>
|
337
|
-
`);
|
338
|
-
}
|
339
|
-
});
|
340
|
-
|
341
|
-
// Serve static files AFTER routes that need SSR
|
342
|
-
app.use(express.static(path.join(__dirname, 'public')));
|
343
|
-
|
344
|
-
// A catch-all route for any other requests
|
345
|
-
app.get('*', (req, res) => {
|
346
|
-
console.log(`Handling catch-all route: ${req.path}`);
|
347
|
-
res.status(404).send(`
|
348
|
-
<!DOCTYPE html>
|
349
|
-
<html>
|
350
|
-
<head>
|
351
|
-
<title>Page Not Found</title>
|
352
|
-
<style>
|
353
|
-
body { font-family: sans-serif; padding: 2rem; }
|
354
|
-
</style>
|
355
|
-
</head>
|
356
|
-
<body>
|
357
|
-
<h1>Page Not Found</h1>
|
358
|
-
<p>The page you requested does not exist.</p>
|
359
|
-
<p><a href="/">Go to home page</a></p>
|
360
|
-
</body>
|
361
|
-
</html>
|
362
|
-
`);
|
363
|
-
});
|
364
|
-
|
365
|
-
// Start the server
|
366
|
-
app.listen(PORT, () => {
|
367
|
-
console.log(`Server running at http://localhost:${PORT}`);
|
368
|
-
console.log(`Open your browser and navigate to http://localhost:${PORT}`);
|
369
|
-
});
|