frontend-hamroun 1.2.83 → 1.2.85
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.js +57 -869
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.client.cjs +1 -1
- package/dist/index.client.cjs.map +1 -1
- package/dist/index.client.js +2 -2
- package/dist/index.client.js.map +1 -1
- package/dist/index.js +116 -136
- package/dist/index.js.map +1 -1
- package/dist/jsx-runtime.cjs.map +1 -1
- package/dist/jsx-runtime.js.map +1 -1
- package/dist/renderer-DaVfBeVi.cjs +2 -0
- package/dist/renderer-DaVfBeVi.cjs.map +1 -0
- package/dist/renderer-nfT7XSpo.js +61 -0
- package/dist/renderer-nfT7XSpo.js.map +1 -0
- package/dist/server-renderer-B5b0Q0ck.cjs +2 -0
- package/dist/server-renderer-B5b0Q0ck.cjs.map +1 -0
- package/dist/{server-renderer-C1WXH-zV.js → server-renderer-C4MB-jAp.js} +6 -39
- package/dist/server-renderer-C4MB-jAp.js.map +1 -0
- package/dist/server-renderer.cjs +1 -1
- package/dist/server-renderer.js +1 -1
- package/package.json +1 -1
- package/templates/basic-app/build.d.ts +2 -0
- package/templates/basic-app/build.d.ts.map +1 -0
- package/templates/basic-app/dev.d.ts +2 -0
- package/templates/basic-app/dev.d.ts.map +1 -0
- package/templates/basic-app/esbuild.config.d.ts +2 -0
- package/templates/basic-app/esbuild.config.d.ts.map +1 -0
- package/templates/basic-app/postcss.config.d.ts +8 -0
- package/templates/basic-app/postcss.config.d.ts.map +1 -0
- package/templates/basic-app/server.d.ts +2 -0
- package/templates/basic-app/server.d.ts.map +1 -0
- package/templates/basic-app/src/App.d.ts +2 -0
- package/templates/basic-app/src/App.d.ts.map +1 -0
- package/templates/basic-app/src/App.js +148 -0
- package/templates/basic-app/src/App.tsx +397 -19
- package/templates/basic-app/src/client.d.ts +2 -0
- package/templates/basic-app/src/client.d.ts.map +1 -0
- package/templates/basic-app/src/client.js +6 -0
- package/templates/basic-app/src/components/Counter.d.ts +4 -0
- package/templates/basic-app/src/components/Counter.d.ts.map +1 -0
- package/templates/basic-app/src/components/Counter.js +9 -0
- package/templates/basic-app/src/jsx-shim.d.ts +8 -0
- package/templates/basic-app/src/jsx-shim.d.ts.map +1 -0
- package/templates/basic-app/src/main.d.ts +2 -0
- package/templates/basic-app/src/main.d.ts.map +1 -0
- package/templates/basic-app/src/main.js +57 -0
- package/templates/basic-app/src/server.d.ts +2 -0
- package/templates/basic-app/src/server.d.ts.map +1 -0
- package/templates/basic-app/tailwind.config.d.ts +9 -0
- package/templates/basic-app/tailwind.config.d.ts.map +1 -0
- package/templates/basic-app/vite.config.d.ts +3 -0
- package/templates/basic-app/vite.config.d.ts.map +1 -0
- package/templates/basic-app/vite.config.js +7 -0
- package/templates/complete-app/api/hello.d.ts +1 -0
- package/templates/complete-app/api/hello.d.ts.map +1 -0
- package/templates/complete-app/client.d.ts +2 -0
- package/templates/complete-app/client.d.ts.map +1 -0
- package/templates/complete-app/lib/frontend-hamroun.d.ts +18 -0
- package/templates/complete-app/lib/frontend-hamroun.d.ts.map +1 -0
- package/templates/complete-app/pages/about.d.ts +7 -0
- package/templates/complete-app/pages/about.d.ts.map +1 -0
- package/templates/complete-app/pages/index.d.ts +7 -0
- package/templates/complete-app/pages/index.d.ts.map +1 -0
- package/templates/complete-app/pages/wasm-demo.d.ts +7 -0
- package/templates/complete-app/pages/wasm-demo.d.ts.map +1 -0
- package/templates/complete-app/public/client.d.ts +17 -0
- package/templates/complete-app/public/client.d.ts.map +1 -0
- package/templates/complete-app/server.d.ts +2 -0
- package/templates/complete-app/server.d.ts.map +1 -0
- package/templates/complete-app/server.js +236 -218
- package/templates/complete-app/src/App.d.ts +2 -0
- package/templates/complete-app/src/App.d.ts.map +1 -0
- package/templates/complete-app/src/App.js +27 -0
- package/templates/complete-app/src/client.d.ts +2 -0
- package/templates/complete-app/src/client.d.ts.map +1 -0
- package/templates/complete-app/src/client.js +52 -0
- package/templates/complete-app/src/pages/index.d.ts +2 -0
- package/templates/complete-app/src/pages/index.d.ts.map +1 -0
- package/templates/complete-app/src/pages/index.js +19 -0
- package/templates/complete-app/src/server.d.ts +2 -0
- package/templates/complete-app/src/server.d.ts.map +1 -0
- package/templates/complete-app/src/server.js +192 -0
- package/templates/complete-app/vite.config.d.ts +3 -0
- package/templates/complete-app/vite.config.d.ts.map +1 -0
- package/templates/complete-app/vite.config.js +29 -57
- package/templates/fullstack-app/api/hello.d.ts +4 -0
- package/templates/fullstack-app/api/hello.d.ts.map +1 -0
- package/templates/fullstack-app/api/hello.js +14 -11
- package/templates/fullstack-app/api/users/[id].d.ts +5 -0
- package/templates/fullstack-app/api/users/[id].d.ts.map +1 -0
- package/templates/fullstack-app/api/users/[id].js +52 -0
- package/templates/fullstack-app/api/users/index.d.ts +4 -0
- package/templates/fullstack-app/api/users/index.d.ts.map +1 -0
- package/templates/fullstack-app/api/users/index.js +25 -0
- package/templates/fullstack-app/build/main.d.ts +211 -0
- package/templates/fullstack-app/build/main.d.ts.map +1 -0
- package/templates/fullstack-app/build.d.ts +2 -0
- package/templates/fullstack-app/build.d.ts.map +1 -0
- package/templates/fullstack-app/build.js +190 -0
- package/templates/fullstack-app/postcss.config.d.ts +5 -0
- package/templates/fullstack-app/postcss.config.d.ts.map +1 -0
- package/templates/fullstack-app/process-tailwind.d.ts +2 -0
- package/templates/fullstack-app/process-tailwind.d.ts.map +1 -0
- package/templates/fullstack-app/public/route-handler.d.ts +1 -0
- package/templates/fullstack-app/public/route-handler.d.ts.map +1 -0
- package/templates/fullstack-app/server.d.ts +2 -0
- package/templates/fullstack-app/server.d.ts.map +1 -0
- package/templates/fullstack-app/server.js +428 -266
- package/templates/fullstack-app/src/client.d.ts +2 -0
- package/templates/fullstack-app/src/client.d.ts.map +1 -0
- package/templates/fullstack-app/src/components/ClientHome.d.ts +1 -0
- package/templates/fullstack-app/src/components/ClientHome.d.ts.map +1 -0
- package/templates/fullstack-app/src/components/ClientHome.js +1 -0
- package/templates/fullstack-app/src/components/ErrorBoundary.d.ts +7 -0
- package/templates/fullstack-app/src/components/ErrorBoundary.d.ts.map +1 -0
- package/templates/fullstack-app/src/components/ErrorBoundary.js +12 -0
- package/templates/fullstack-app/src/components/Layout.d.ts +7 -0
- package/templates/fullstack-app/src/components/Layout.d.ts.map +1 -0
- package/templates/fullstack-app/src/components/Layout.js +4 -0
- package/templates/fullstack-app/src/components/StateDemo.d.ts +2 -0
- package/templates/fullstack-app/src/components/StateDemo.d.ts.map +1 -0
- package/templates/fullstack-app/src/components/StateDemo.js +86 -0
- package/templates/fullstack-app/src/components/UserList.d.ts +11 -0
- package/templates/fullstack-app/src/components/UserList.d.ts.map +1 -0
- package/templates/fullstack-app/src/components/UserList.js +7 -0
- package/templates/fullstack-app/src/config.d.ts +29 -0
- package/templates/fullstack-app/src/config.d.ts.map +1 -0
- package/templates/fullstack-app/src/config.js +36 -0
- package/templates/fullstack-app/src/data/api.d.ts +35 -0
- package/templates/fullstack-app/src/data/api.d.ts.map +1 -0
- package/templates/fullstack-app/src/data/api.js +173 -0
- package/templates/fullstack-app/src/main.d.ts +7 -0
- package/templates/fullstack-app/src/main.d.ts.map +1 -0
- package/templates/fullstack-app/src/main.js +130 -0
- package/templates/fullstack-app/src/middleware.d.ts +10 -0
- package/templates/fullstack-app/src/middleware.d.ts.map +1 -0
- package/templates/fullstack-app/src/middleware.js +14 -0
- package/templates/fullstack-app/src/pages/404.d.ts +4 -0
- package/templates/fullstack-app/src/pages/404.d.ts.map +1 -0
- package/templates/fullstack-app/src/pages/404.js +4 -0
- package/templates/fullstack-app/src/pages/[id].d.ts +1 -0
- package/templates/fullstack-app/src/pages/[id].d.ts.map +1 -0
- package/templates/fullstack-app/src/pages/[id].js +1 -0
- package/templates/fullstack-app/src/pages/_app.d.ts +6 -0
- package/templates/fullstack-app/src/pages/_app.d.ts.map +1 -0
- package/templates/fullstack-app/src/pages/_app.js +6 -0
- package/templates/fullstack-app/src/pages/_document.d.ts +7 -0
- package/templates/fullstack-app/src/pages/_document.d.ts.map +1 -0
- package/templates/fullstack-app/src/pages/_document.js +4 -0
- package/templates/fullstack-app/src/pages/_error.d.ts +4 -0
- package/templates/fullstack-app/src/pages/_error.d.ts.map +1 -0
- package/templates/fullstack-app/src/pages/_error.js +8 -0
- package/templates/fullstack-app/src/pages/about/index.d.ts +5 -0
- package/templates/fullstack-app/src/pages/about/index.d.ts.map +1 -0
- package/templates/fullstack-app/src/pages/about/index.js +6 -0
- package/templates/fullstack-app/src/pages/about.d.ts +10 -0
- package/templates/fullstack-app/src/pages/about.d.ts.map +1 -0
- package/templates/fullstack-app/src/pages/about.js +21 -0
- package/templates/fullstack-app/src/pages/api/users/[id].d.ts +6 -0
- package/templates/fullstack-app/src/pages/api/users/[id].d.ts.map +1 -0
- package/templates/fullstack-app/src/pages/api/users/[id].js +51 -0
- package/templates/fullstack-app/src/pages/api/users/index.d.ts +4 -0
- package/templates/fullstack-app/src/pages/api/users/index.d.ts.map +1 -0
- package/templates/fullstack-app/src/pages/api/users/index.js +33 -0
- package/templates/fullstack-app/src/pages/index.d.ts +21 -0
- package/templates/fullstack-app/src/pages/index.d.ts.map +1 -0
- package/templates/fullstack-app/src/pages/index.js +66 -0
- package/templates/fullstack-app/src/pages/users/[id].d.ts +38 -0
- package/templates/fullstack-app/src/pages/users/[id].d.ts.map +1 -0
- package/templates/fullstack-app/src/pages/users/[id].js +79 -0
- package/templates/fullstack-app/src/pages/users.d.ts +14 -0
- package/templates/fullstack-app/src/pages/users.d.ts.map +1 -0
- package/templates/fullstack-app/src/pages/users.js +14 -0
- package/templates/fullstack-app/src/pages/wasm-demo.d.ts +1 -0
- package/templates/fullstack-app/src/pages/wasm-demo.d.ts.map +1 -0
- package/templates/fullstack-app/src/pages/wasm-demo.js +2 -0
- package/templates/fullstack-app/src/router.d.ts +22 -0
- package/templates/fullstack-app/src/router.d.ts.map +1 -0
- package/templates/fullstack-app/src/router.js +210 -0
- package/templates/fullstack-app/tailwind.config.d.ts +3 -0
- package/templates/fullstack-app/tailwind.config.d.ts.map +1 -0
- package/templates/fullstack-app/vite.config.d.ts +3 -0
- package/templates/fullstack-app/vite.config.d.ts.map +1 -0
- package/templates/ssr-template/dist/client/assets/main-D-VH3xOb.d.ts +2 -0
- package/templates/ssr-template/dist/client/assets/main-D-VH3xOb.d.ts.map +1 -0
- package/templates/ssr-template/dist/client.d.ts +85 -0
- package/templates/ssr-template/dist/client.d.ts.map +1 -0
- package/templates/ssr-template/dist/server.d.ts +2 -0
- package/templates/ssr-template/dist/server.d.ts.map +1 -0
- package/templates/ssr-template/esbuild.config.d.ts +2 -0
- package/templates/ssr-template/esbuild.config.d.ts.map +1 -0
- package/templates/ssr-template/jsx-shim.d.ts +2 -0
- package/templates/ssr-template/jsx-shim.d.ts.map +1 -0
- package/templates/ssr-template/src/App.d.ts +2 -0
- package/templates/ssr-template/src/App.d.ts.map +1 -0
- package/templates/ssr-template/src/App.js +625 -0
- package/templates/ssr-template/src/App.tsx +43 -18
- package/templates/ssr-template/src/client.d.ts +2 -0
- package/templates/ssr-template/src/client.d.ts.map +1 -0
- package/templates/ssr-template/src/client.js +3 -0
- package/templates/ssr-template/src/server.d.ts +2 -0
- package/templates/ssr-template/src/server.d.ts.map +1 -0
- package/templates/ssr-template/src/server.js +29 -0
- package/templates/ssr-template/vite.config.d.ts +3 -0
- package/templates/ssr-template/vite.config.d.ts.map +1 -0
- package/templates/ssr-template/vite.config.js +30 -0
- package/templates/wasm/build-wasm.d.ts +2 -0
- package/templates/wasm/build-wasm.d.ts.map +1 -0
- package/templates/wasm/dist/assets/index-BNqTDBdE.d.ts +30 -0
- package/templates/wasm/dist/assets/index-BNqTDBdE.d.ts.map +1 -0
- package/templates/wasm/dist/wasm_exec.d.ts +1 -0
- package/templates/wasm/dist/wasm_exec.d.ts.map +1 -0
- package/templates/wasm/esbuild.config.d.ts +2 -0
- package/templates/wasm/esbuild.config.d.ts.map +1 -0
- package/templates/wasm/go/wasm_exec.d.ts +1 -0
- package/templates/wasm/go/wasm_exec.d.ts.map +1 -0
- package/templates/wasm/jsx-shim.d.ts +5 -0
- package/templates/wasm/jsx-shim.d.ts.map +1 -0
- package/templates/wasm/public/wasm_exec.d.ts +1 -0
- package/templates/wasm/public/wasm_exec.d.ts.map +1 -0
- package/templates/wasm/src/App.d.ts +2 -0
- package/templates/wasm/src/App.d.ts.map +1 -0
- package/templates/wasm/src/App.js +381 -0
- package/templates/wasm/src/client.d.ts +2 -0
- package/templates/wasm/src/client.d.ts.map +1 -0
- package/templates/wasm/src/client.js +210 -0
- package/templates/wasm/src/index.d.ts +2 -0
- package/templates/wasm/src/index.d.ts.map +1 -0
- package/templates/wasm/src/index.js +20 -0
- package/templates/wasm/src/server.d.ts +2 -0
- package/templates/wasm/src/server.d.ts.map +1 -0
- package/templates/wasm/src/server.js +131 -0
- package/templates/wasm/vite.config.d.ts +3 -0
- package/templates/wasm/vite.config.d.ts.map +1 -0
- package/templates/wasm/vite.config.js +36 -0
- package/templates/wasm/wasm-loader.d.ts +6 -0
- package/templates/wasm/wasm-loader.d.ts.map +1 -0
- package/dist/renderer-BL3gq8cW.cjs +0 -2
- package/dist/renderer-BL3gq8cW.cjs.map +0 -1
- package/dist/renderer-Dyy-o05F.js +0 -52
- package/dist/renderer-Dyy-o05F.js.map +0 -1
- package/dist/server-renderer-C1WXH-zV.js.map +0 -1
- package/dist/server-renderer-Chs-nmJm.cjs +0 -2
- package/dist/server-renderer-Chs-nmJm.cjs.map +0 -1
@@ -0,0 +1,17 @@
|
|
1
|
+
declare function jsx(type: any, props: any, ...children: any[]): {
|
2
|
+
type: any;
|
3
|
+
props: any;
|
4
|
+
};
|
5
|
+
declare function jsx(type: any, props: any, ...args: any[]): {
|
6
|
+
type: any;
|
7
|
+
props: any;
|
8
|
+
};
|
9
|
+
declare function jsx(type: any, props: any, ...args: any[]): {
|
10
|
+
type: any;
|
11
|
+
props: any;
|
12
|
+
};
|
13
|
+
declare function hydrate(element: any, container: any): void;
|
14
|
+
declare function hydrate(element: any, container: any): Promise<void>;
|
15
|
+
declare function hydrate(element: any, container: any): Promise<void>;
|
16
|
+
declare const Fragment: typeof Fragment;
|
17
|
+
//# sourceMappingURL=client.d.ts.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["client.js"],"names":[],"mappings":"AAGA;;;EAMC;;;;;;;;;AAMD,6DAsCC;;;AAzCD,wCAA8C"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["server.ts"],"names":[],"mappings":""}
|
@@ -1,226 +1,244 @@
|
|
1
|
-
import express from 'express';
|
2
|
-
import
|
3
|
-
import {
|
4
|
-
import
|
5
|
-
|
6
|
-
import {
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
//
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
}
|
41
|
-
|
42
|
-
//
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
//
|
52
|
-
app.get('
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
}
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
//
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
//
|
81
|
-
const
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
const
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
}
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
};
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
// Skip static assets - check file extensions that should be handled as static
|
132
|
-
if (req.path.match(/\.(js|css|ico|png|jpg|jpeg|gif|svg|wasm|txt|pdf|json|map)$/)) {
|
133
|
-
return next();
|
134
|
-
}
|
135
|
-
|
136
|
-
// Try to find the matching component
|
137
|
-
const routeResult = await getComponentPath(req.path);
|
138
|
-
|
139
|
-
if (!routeResult) {
|
140
|
-
return res.status(404).send(`
|
1
|
+
import express from 'express';
|
2
|
+
import { fileURLToPath } from 'url';
|
3
|
+
import { dirname, join } from 'path';
|
4
|
+
import { renderToString } from 'frontend-hamroun';
|
5
|
+
import { Database } from 'frontend-hamroun';
|
6
|
+
import { AuthService } from 'frontend-hamroun';
|
7
|
+
import { requestLogger, errorHandler, notFoundHandler, rateLimit } from 'frontend-hamroun';
|
8
|
+
import dotenv from 'dotenv';
|
9
|
+
// Load environment variables
|
10
|
+
dotenv.config();
|
11
|
+
// Get directory name in ESM
|
12
|
+
const __filename = fileURLToPath(import.meta.url);
|
13
|
+
const __dirname = dirname(__filename);
|
14
|
+
// Create Express app
|
15
|
+
const app = express();
|
16
|
+
const port = process.env.PORT ? parseInt(process.env.PORT) : 3000;
|
17
|
+
// Add middleware
|
18
|
+
app.use(express.json());
|
19
|
+
app.use(express.urlencoded({ extended: true }));
|
20
|
+
app.use(requestLogger);
|
21
|
+
// Rate limiting for API routes
|
22
|
+
app.use('/api', rateLimit({
|
23
|
+
windowMs: 15 * 60 * 1000, // 15 minutes
|
24
|
+
max: 100 // limit each IP to 100 requests per windowMs
|
25
|
+
}));
|
26
|
+
// Configure database if connection string is provided
|
27
|
+
let db = null;
|
28
|
+
if (process.env.DATABASE_URL) {
|
29
|
+
db = new Database({
|
30
|
+
url: process.env.DATABASE_URL,
|
31
|
+
type: (process.env.DATABASE_TYPE || 'mongodb')
|
32
|
+
});
|
33
|
+
// Connect to database
|
34
|
+
try {
|
35
|
+
await db.connect();
|
36
|
+
console.log('Database connected successfully');
|
37
|
+
}
|
38
|
+
catch (error) {
|
39
|
+
console.error('Database connection failed:', error);
|
40
|
+
}
|
41
|
+
}
|
42
|
+
// Configure auth if secret is provided
|
43
|
+
let auth = null;
|
44
|
+
if (process.env.JWT_SECRET) {
|
45
|
+
auth = new AuthService({
|
46
|
+
secret: process.env.JWT_SECRET,
|
47
|
+
expiresIn: process.env.JWT_EXPIRES_IN || '24h'
|
48
|
+
});
|
49
|
+
// Add auth middleware
|
50
|
+
app.use(auth.initialize());
|
51
|
+
// Example protected route
|
52
|
+
app.get('/api/protected', auth.requireAuth(), (req, res) => {
|
53
|
+
res.json({ message: 'Protected route accessed successfully' });
|
54
|
+
});
|
55
|
+
// Example role-based protection
|
56
|
+
app.get('/api/admin', auth.requireRoles(['admin']), (req, res) => {
|
57
|
+
res.json({ message: 'Admin route accessed successfully' });
|
58
|
+
});
|
59
|
+
// Login route
|
60
|
+
app.post('/api/login', async (req, res) => {
|
61
|
+
const { username, password } = req.body;
|
62
|
+
// In a real app, fetch user from database
|
63
|
+
const user = { id: 1, username, roles: ['user'] };
|
64
|
+
const token = auth.generateToken(user);
|
65
|
+
res.json({ token, user: { id: user.id, username: user.username, roles: user.roles } });
|
66
|
+
});
|
67
|
+
}
|
68
|
+
// Serve static files from public directory
|
69
|
+
app.use(express.static(join(__dirname, 'public')));
|
70
|
+
// API endpoint example
|
71
|
+
app.get('/api/page-data', (req, res) => {
|
72
|
+
res.json({
|
73
|
+
title: 'Server-side Data',
|
74
|
+
content: 'This data was fetched from the server',
|
75
|
+
timestamp: new Date().toISOString()
|
76
|
+
});
|
77
|
+
});
|
78
|
+
// Meta tag generation function (using local logic for simplicity)
|
79
|
+
async function generateMetaTags(pageContent) {
|
80
|
+
// Extract title from page content
|
81
|
+
const title = pageContent.split('\n')[0].replace(/[#*]/g, '').trim() ||
|
82
|
+
'Frontend Hamroun SSR Page';
|
83
|
+
// Generate description from content
|
84
|
+
const description = pageContent.substring(0, 150) + '...';
|
85
|
+
// Extract keywords
|
86
|
+
const keywords = pageContent
|
87
|
+
.toLowerCase()
|
88
|
+
.replace(/[^\w\s]/g, '')
|
89
|
+
.split(/\s+/)
|
90
|
+
.filter(w => w.length > 3)
|
91
|
+
.slice(0, 5)
|
92
|
+
.join(', ');
|
93
|
+
return {
|
94
|
+
title,
|
95
|
+
description,
|
96
|
+
keywords
|
97
|
+
};
|
98
|
+
}
|
99
|
+
// Helper function to check if file exists
|
100
|
+
async function fileExists(path) {
|
101
|
+
try {
|
102
|
+
const fs = await import('fs/promises');
|
103
|
+
await fs.access(path);
|
104
|
+
return true;
|
105
|
+
}
|
106
|
+
catch {
|
107
|
+
return false;
|
108
|
+
}
|
109
|
+
}
|
110
|
+
// Implement basic SSR without relying on complex server functionality
|
111
|
+
app.get('*', async (req, res) => {
|
112
|
+
try {
|
113
|
+
// Import the page component
|
114
|
+
const pagesDir = join(__dirname, 'src', 'pages');
|
115
|
+
let componentPath;
|
116
|
+
// Map URL path to component file
|
117
|
+
if (req.path === '/') {
|
118
|
+
componentPath = join(pagesDir, 'index.js');
|
119
|
+
}
|
120
|
+
else {
|
121
|
+
componentPath = join(pagesDir, `${req.path}.js`);
|
122
|
+
// Check if it's a directory with index.js
|
123
|
+
if (!await fileExists(componentPath)) {
|
124
|
+
componentPath = join(pagesDir, req.path, 'index.js');
|
125
|
+
}
|
126
|
+
}
|
127
|
+
// If component doesn't exist, return 404
|
128
|
+
if (!await fileExists(componentPath)) {
|
129
|
+
return res.status(404).send(`
|
141
130
|
<!DOCTYPE html>
|
142
131
|
<html>
|
143
132
|
<head>
|
144
133
|
<title>404 - Page Not Found</title>
|
145
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
146
|
-
<link href="/styles.css" rel="stylesheet" type="text/css">
|
147
134
|
</head>
|
148
135
|
<body>
|
149
|
-
<
|
150
|
-
|
151
|
-
<h1>404 - Page Not Found</h1>
|
152
|
-
<p>The page you requested could not be found.</p>
|
153
|
-
<a href="/" class="button">Go to Home</a>
|
154
|
-
</div>
|
155
|
-
</div>
|
156
|
-
<script id="__APP_DATA__" type="application/json">${JSON.stringify({
|
157
|
-
path: req.path,
|
158
|
-
error: 'not_found'
|
159
|
-
})}</script>
|
160
|
-
<script type="module" src="/client.js"></script>
|
136
|
+
<h1>404 - Page Not Found</h1>
|
137
|
+
<p>The page you requested does not exist.</p>
|
161
138
|
</body>
|
162
139
|
</html>
|
163
|
-
`);
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
<
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
</
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
140
|
+
`);
|
141
|
+
}
|
142
|
+
// Import the component
|
143
|
+
const { default: PageComponent } = await import(componentPath);
|
144
|
+
// Generate page content for meta tags
|
145
|
+
const pageContent = `
|
146
|
+
Frontend Hamroun SSR Page
|
147
|
+
This is a server-rendered page using the Frontend Hamroun framework.
|
148
|
+
Path: ${req.path}
|
149
|
+
Timestamp: ${new Date().toISOString()}
|
150
|
+
`;
|
151
|
+
// Generate meta tags
|
152
|
+
const metaTags = await generateMetaTags(pageContent);
|
153
|
+
// Render the component to string
|
154
|
+
const content = renderToString(PageComponent());
|
155
|
+
// Send the HTML response
|
156
|
+
res.send(`
|
157
|
+
<!DOCTYPE html>
|
158
|
+
<html lang="en">
|
159
|
+
<head>
|
160
|
+
<meta charset="UTF-8">
|
161
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
162
|
+
|
163
|
+
<!-- Generated Meta Tags -->
|
164
|
+
<title>${metaTags.title}</title>
|
165
|
+
<meta name="description" content="${metaTags.description}">
|
166
|
+
<meta name="keywords" content="${metaTags.keywords}">
|
167
|
+
|
168
|
+
<!-- Open Graph Meta Tags -->
|
169
|
+
<meta property="og:title" content="${metaTags.title}">
|
170
|
+
<meta property="og:description" content="${metaTags.description}">
|
171
|
+
<meta property="og:type" content="website">
|
172
|
+
<meta property="og:url" content="${req.protocol}://${req.get('host')}${req.originalUrl}">
|
173
|
+
|
174
|
+
<!-- Import Tailwind-like styles for quick styling -->
|
175
|
+
<link href="https://cdn.jsdelivr.net/npm/daisyui@3.7.4/dist/full.css" rel="stylesheet" type="text/css" />
|
176
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
177
|
+
|
178
|
+
<!-- Client-side script for hydration -->
|
179
|
+
<script type="module" src="/assets/client.js"></script>
|
180
|
+
</head>
|
181
|
+
<body>
|
182
|
+
<div id="app">${content}</div>
|
183
|
+
|
184
|
+
<!-- Add initial state for hydration -->
|
185
|
+
<script>
|
186
|
+
window.__INITIAL_STATE__ = ${JSON.stringify({
|
187
|
+
path: req.path,
|
188
|
+
timestamp: new Date().toISOString(),
|
189
|
+
metaTags
|
190
|
+
})};
|
191
|
+
</script>
|
192
|
+
</body>
|
193
|
+
</html>
|
194
|
+
`);
|
195
|
+
}
|
196
|
+
catch (error) {
|
197
|
+
console.error('Error rendering page:', error);
|
198
|
+
res.status(500).send(`
|
199
|
+
<!DOCTYPE html>
|
200
|
+
<html>
|
201
|
+
<head>
|
202
|
+
<title>500 - Server Error</title>
|
203
|
+
</head>
|
204
|
+
<body>
|
205
|
+
<h1>500 - Server Error</h1>
|
206
|
+
<p>There was an error processing your request.</p>
|
207
|
+
${process.env.NODE_ENV === 'development' ? `<pre>${error.stack}</pre>` : ''}
|
208
|
+
</body>
|
209
|
+
</html>
|
210
|
+
`);
|
211
|
+
}
|
212
|
+
});
|
213
|
+
// Add error handler middleware
|
214
|
+
app.use(errorHandler);
|
215
|
+
// Add not found handler for API routes that weren't caught
|
216
|
+
app.use(notFoundHandler);
|
217
|
+
// Graceful shutdown function to close database connections
|
218
|
+
function gracefulShutdown() {
|
219
|
+
console.log('Shutting down server...');
|
220
|
+
// Close database connection if it exists
|
221
|
+
if (db) {
|
222
|
+
db.disconnect()
|
223
|
+
.then(() => console.log('Database disconnected'))
|
224
|
+
.catch(err => console.error('Error disconnecting from database:', err))
|
225
|
+
.finally(() => process.exit(0));
|
226
|
+
}
|
227
|
+
else {
|
228
|
+
process.exit(0);
|
229
|
+
}
|
230
|
+
}
|
231
|
+
// Start the server
|
232
|
+
const server = app.listen(port, () => {
|
233
|
+
console.log(`Server running at http://localhost:${port}`);
|
234
|
+
console.log(`Available API endpoints:`);
|
235
|
+
console.log(` - GET /api/page-data`);
|
236
|
+
console.log(` - POST /api/login`);
|
237
|
+
if (process.env.JWT_SECRET) {
|
238
|
+
console.log(` - GET /api/protected (requires authentication)`);
|
239
|
+
console.log(` - GET /api/admin (requires admin role)`);
|
240
|
+
}
|
241
|
+
});
|
242
|
+
// Handle graceful shutdown
|
243
|
+
process.on('SIGINT', gracefulShutdown);
|
244
|
+
process.on('SIGTERM', gracefulShutdown);
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"App.d.ts","sourceRoot":"","sources":["App.tsx"],"names":[],"mappings":"AAKA,wBAAgB,GAAG,gBAqDlB"}
|
@@ -0,0 +1,27 @@
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "frontend-hamroun/jsx-runtime";
|
2
|
+
import { useState, useEffect, useMemo, useRef, createContext } from 'frontend-hamroun';
|
3
|
+
// Create a theme context
|
4
|
+
const ThemeContext = createContext('light');
|
5
|
+
export function App() {
|
6
|
+
// Initialize with a default state that works on both server and client
|
7
|
+
const [count, setCount] = useState(0);
|
8
|
+
const [theme, setTheme] = useState('light');
|
9
|
+
const renderCount = useRef(0);
|
10
|
+
// Client-side only effect
|
11
|
+
useEffect(() => {
|
12
|
+
if (typeof window !== 'undefined') {
|
13
|
+
renderCount.current += 1;
|
14
|
+
console.log('Component rendered', renderCount.current, 'times');
|
15
|
+
}
|
16
|
+
return () => console.log('Component unmounting');
|
17
|
+
}, [count]);
|
18
|
+
// Memoized value
|
19
|
+
const doubled = useMemo(() => count * 2, [count]);
|
20
|
+
return (_jsx(ThemeContext.Provider, { value: theme, children: _jsxs("div", { style: {
|
21
|
+
padding: '20px',
|
22
|
+
backgroundColor: theme === 'dark' ? '#333' : '#fff',
|
23
|
+
color: theme === 'dark' ? '#fff' : '#333'
|
24
|
+
}, children: [_jsx("h1", { children: "Server-Side Rendered App" }), _jsxs("div", { children: [_jsx("button", { onClick: () => setCount(count - 1), "data-action": "decrement", children: "-" }), _jsx("span", { style: { margin: '0 10px' }, children: count }), _jsx("button", { onClick: () => setCount(count + 1), "data-action": "increment", children: "+" })] }), _jsxs("p", { children: ["Doubled value: ", doubled] }), typeof window !== 'undefined' && (_jsxs("p", { children: ["Render count: ", renderCount.current] })), _jsxs("button", { onClick: () => setTheme(theme === 'light' ? 'dark' : 'light'), style: { marginTop: '10px' }, children: ["Toggle Theme (", theme, ")"] }), _jsx("script", { dangerouslySetInnerHTML: {
|
25
|
+
__html: `window.__INITIAL_STATE__ = ${JSON.stringify({ count: 0, theme: 'light' })};`
|
26
|
+
} })] }) }));
|
27
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["client.ts"],"names":[],"mappings":""}
|
@@ -0,0 +1,52 @@
|
|
1
|
+
import { hydrate, jsx } from 'frontend-hamroun';
|
2
|
+
// Dynamically import the appropriate page component
|
3
|
+
async function hydratePage() {
|
4
|
+
try {
|
5
|
+
// Get initial state from server
|
6
|
+
const initialState = window.__INITIAL_STATE__ || {};
|
7
|
+
// Get current path
|
8
|
+
const path = initialState.route || window.location.pathname;
|
9
|
+
const normalizedPath = path === '/' ? '/index' : path;
|
10
|
+
// Create path to module
|
11
|
+
const modulePath = `.${normalizedPath.replace(/\/$/, '')}.js`;
|
12
|
+
try {
|
13
|
+
// Dynamically import the component
|
14
|
+
const module = await import(`./pages${normalizedPath}.js`).catch(() => import(`./pages${normalizedPath}/index.js`));
|
15
|
+
const PageComponent = module.default;
|
16
|
+
// Find the root element
|
17
|
+
const rootElement = document.getElementById('root');
|
18
|
+
if (rootElement && PageComponent) {
|
19
|
+
// Hydrate the application with the same params from the server
|
20
|
+
hydrate(jsx(PageComponent, { params: initialState.params || {} }), rootElement);
|
21
|
+
console.log('Hydration complete');
|
22
|
+
}
|
23
|
+
else {
|
24
|
+
console.error('Could not find root element or page component');
|
25
|
+
}
|
26
|
+
}
|
27
|
+
catch (importError) {
|
28
|
+
console.error('Error importing page component:', importError);
|
29
|
+
// Fallback to App component if available
|
30
|
+
try {
|
31
|
+
const { App } = await import('./App.js');
|
32
|
+
const rootElement = document.getElementById('root');
|
33
|
+
if (rootElement && App) {
|
34
|
+
hydrate(jsx(App, {}), rootElement);
|
35
|
+
console.log('Fallback hydration complete');
|
36
|
+
}
|
37
|
+
}
|
38
|
+
catch (fallbackError) {
|
39
|
+
console.error('Fallback hydration failed:', fallbackError);
|
40
|
+
}
|
41
|
+
}
|
42
|
+
}
|
43
|
+
catch (error) {
|
44
|
+
console.error('Hydration error:', error);
|
45
|
+
}
|
46
|
+
}
|
47
|
+
// Add global variable for JSX
|
48
|
+
window.jsx = jsx;
|
49
|
+
// Hydrate when DOM is ready
|
50
|
+
document.addEventListener('DOMContentLoaded', hydratePage);
|
51
|
+
// Handle client-side navigation (if implemented)
|
52
|
+
window.addEventListener('popstate', hydratePage);
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.tsx"],"names":[],"mappings":"AAGA,MAAM,CAAC,OAAO,UAAU,QAAQ,gBA+C/B"}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "frontend-hamroun/jsx-runtime";
|
2
|
+
import { useState, useEffect } from 'frontend-hamroun';
|
3
|
+
// This component will be rendered on both server and client
|
4
|
+
export default function HomePage() {
|
5
|
+
const [count, setCount] = useState(0);
|
6
|
+
const [serverTime, setServerTime] = useState('');
|
7
|
+
// This effect only runs on the client after hydration
|
8
|
+
useEffect(() => {
|
9
|
+
// Set server render time (only when hydrating)
|
10
|
+
if (!serverTime) {
|
11
|
+
setServerTime('Client-side hydration complete');
|
12
|
+
}
|
13
|
+
// Simple cleanup function
|
14
|
+
return () => {
|
15
|
+
console.log('Component unmounting');
|
16
|
+
};
|
17
|
+
}, []);
|
18
|
+
return (_jsx("div", { id: "app", children: _jsx("div", { className: "hero min-h-screen bg-base-200", children: _jsx("div", { className: "hero-content text-center", children: _jsxs("div", { className: "max-w-md", children: [_jsx("h1", { className: "text-5xl font-bold", children: "Frontend Hamroun SSR" }), _jsx("p", { className: "py-6", children: "This page was rendered on the server and hydrated on the client." }), _jsxs("div", { className: "my-4 p-4 bg-base-300 rounded-lg", children: [_jsxs("p", { children: ["Counter: ", count] }), _jsx("button", { className: "btn btn-primary mt-2", onClick: () => setCount(count + 1), children: "Increment" })] }), _jsx("div", { className: "text-sm opacity-70 mt-4", children: serverTime ? serverTime : `Server rendered at: ${new Date().toISOString()}` })] }) }) }) }));
|
19
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["server.ts"],"names":[],"mappings":""}
|