frontend-hamroun 1.2.22 → 1.2.24
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/dist/index.js +69 -73
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +69 -73
- package/dist/index.mjs.map +1 -1
- package/dist/server-renderer.d.ts +5 -1
- package/package.json +18 -3
- package/templates/ssr-template/package-lock.json +3544 -0
- package/templates/ssr-template/package.json +3 -16
- package/templates/ssr-template/public/index.html +29 -8
- package/templates/ssr-template/readme.md +63 -0
- package/templates/ssr-template/server.js +577 -0
- package/templates/ssr-template/server.ts +111 -34
- package/templates/ssr-template/src/client.ts +29 -0
- package/templates/ssr-template/vite.config.js +9 -12
@@ -1,46 +1,123 @@
|
|
1
|
-
import { Server, renderComponent } from 'frontend-hamroun/server';
|
2
|
-
import { join } from 'path';
|
3
1
|
import express from 'express';
|
2
|
+
import { fileURLToPath } from 'url';
|
3
|
+
import { dirname, join } from 'path';
|
4
|
+
import { renderToString } from 'frontend-hamroun';
|
4
5
|
|
5
|
-
|
6
|
+
// Get directory name in ESM
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
8
|
+
const __dirname = dirname(__filename);
|
9
|
+
|
10
|
+
// Create Express app
|
11
|
+
const app = express();
|
12
|
+
const port = process.env.PORT ? parseInt(process.env.PORT) : 3000;
|
13
|
+
|
14
|
+
// Serve static files from public directory
|
15
|
+
app.use(express.static(join(__dirname, 'public')));
|
16
|
+
|
17
|
+
// API endpoint example
|
18
|
+
app.get('/api/page-data', (req, res) => {
|
19
|
+
res.json({
|
20
|
+
title: 'Server-side Data',
|
21
|
+
content: 'This data was fetched from the server',
|
22
|
+
timestamp: new Date().toISOString()
|
23
|
+
});
|
24
|
+
});
|
25
|
+
|
26
|
+
// Implement basic SSR without relying on complex server functionality
|
27
|
+
app.get('*', async (req, res) => {
|
6
28
|
try {
|
7
|
-
//
|
8
|
-
const
|
9
|
-
|
10
|
-
pagesDir: './src/pages',
|
11
|
-
staticDir: './public',
|
12
|
-
|
13
|
-
// Enable CORS for API endpoints
|
14
|
-
enableCors: true
|
15
|
-
});
|
29
|
+
// Import the page component
|
30
|
+
const pagesDir = join(__dirname, 'src', 'pages');
|
31
|
+
let componentPath;
|
16
32
|
|
17
|
-
//
|
18
|
-
|
33
|
+
// Map URL path to component file
|
34
|
+
if (req.path === '/') {
|
35
|
+
componentPath = join(pagesDir, 'index.js');
|
36
|
+
} else {
|
37
|
+
componentPath = join(pagesDir, `${req.path}.js`);
|
38
|
+
// Check if it's a directory with index.js
|
39
|
+
if (!await fileExists(componentPath)) {
|
40
|
+
componentPath = join(pagesDir, req.path, 'index.js');
|
41
|
+
}
|
42
|
+
}
|
19
43
|
|
20
|
-
//
|
21
|
-
|
22
|
-
res.
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
44
|
+
// If component doesn't exist, return 404
|
45
|
+
if (!await fileExists(componentPath)) {
|
46
|
+
return res.status(404).send(`
|
47
|
+
<!DOCTYPE html>
|
48
|
+
<html>
|
49
|
+
<head>
|
50
|
+
<title>404 - Page Not Found</title>
|
51
|
+
</head>
|
52
|
+
<body>
|
53
|
+
<h1>404 - Page Not Found</h1>
|
54
|
+
<p>The page you requested does not exist.</p>
|
55
|
+
</body>
|
56
|
+
</html>
|
57
|
+
`);
|
58
|
+
}
|
28
59
|
|
29
|
-
//
|
30
|
-
await
|
60
|
+
// Import the component
|
61
|
+
const { default: PageComponent } = await import(componentPath);
|
31
62
|
|
32
|
-
|
63
|
+
// Render the component to string
|
64
|
+
const content = renderToString(PageComponent());
|
33
65
|
|
34
|
-
//
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
66
|
+
// Send the HTML response
|
67
|
+
res.send(`
|
68
|
+
<!DOCTYPE html>
|
69
|
+
<html lang="en">
|
70
|
+
<head>
|
71
|
+
<meta charset="UTF-8">
|
72
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
73
|
+
<title>Frontend Hamroun SSR</title>
|
74
|
+
<!-- Import Tailwind-like styles for quick styling -->
|
75
|
+
<link href="https://cdn.jsdelivr.net/npm/daisyui@3.7.4/dist/full.css" rel="stylesheet" type="text/css" />
|
76
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
77
|
+
<!-- Client-side script for hydration -->
|
78
|
+
<script type="module" src="/assets/client.js"></script>
|
79
|
+
</head>
|
80
|
+
<body>
|
81
|
+
<div id="app">${content}</div>
|
82
|
+
</body>
|
83
|
+
</html>
|
84
|
+
`);
|
40
85
|
} catch (error) {
|
41
|
-
console.error('
|
42
|
-
|
86
|
+
console.error('Error rendering page:', error);
|
87
|
+
res.status(500).send(`
|
88
|
+
<!DOCTYPE html>
|
89
|
+
<html>
|
90
|
+
<head>
|
91
|
+
<title>500 - Server Error</title>
|
92
|
+
</head>
|
93
|
+
<body>
|
94
|
+
<h1>500 - Server Error</h1>
|
95
|
+
<p>There was an error processing your request.</p>
|
96
|
+
${process.env.NODE_ENV === 'development' ? `<pre>${error.stack}</pre>` : ''}
|
97
|
+
</body>
|
98
|
+
</html>
|
99
|
+
`);
|
100
|
+
}
|
101
|
+
});
|
102
|
+
|
103
|
+
// Helper function to check if file exists
|
104
|
+
async function fileExists(path) {
|
105
|
+
try {
|
106
|
+
const fs = await import('fs/promises');
|
107
|
+
await fs.access(path);
|
108
|
+
return true;
|
109
|
+
} catch {
|
110
|
+
return false;
|
43
111
|
}
|
44
112
|
}
|
45
113
|
|
46
|
-
|
114
|
+
// Start the server
|
115
|
+
app.listen(port, () => {
|
116
|
+
console.log(`Server running at http://localhost:${port}`);
|
117
|
+
});
|
118
|
+
|
119
|
+
// Handle graceful shutdown
|
120
|
+
process.on('SIGINT', () => {
|
121
|
+
console.log('Shutting down server...');
|
122
|
+
process.exit(0);
|
123
|
+
});
|
@@ -0,0 +1,29 @@
|
|
1
|
+
import { hydrate } from 'frontend-hamroun';
|
2
|
+
|
3
|
+
// Dynamically import the appropriate page component
|
4
|
+
async function hydratePage() {
|
5
|
+
try {
|
6
|
+
// Get current path
|
7
|
+
const path = window.location.pathname === '/' ? '/index' : window.location.pathname;
|
8
|
+
|
9
|
+
// Dynamically import the component
|
10
|
+
const module = await import(`./pages${path}.js`);
|
11
|
+
const PageComponent = module.default;
|
12
|
+
|
13
|
+
// Find the root element
|
14
|
+
const rootElement = document.getElementById('app');
|
15
|
+
|
16
|
+
if (rootElement && PageComponent) {
|
17
|
+
// Hydrate the application
|
18
|
+
hydrate(PageComponent(), rootElement);
|
19
|
+
console.log('Hydration complete');
|
20
|
+
} else {
|
21
|
+
console.error('Could not find root element or page component');
|
22
|
+
}
|
23
|
+
} catch (error) {
|
24
|
+
console.error('Hydration error:', error);
|
25
|
+
}
|
26
|
+
}
|
27
|
+
|
28
|
+
// Hydrate when DOM is ready
|
29
|
+
document.addEventListener('DOMContentLoaded', hydratePage);
|
@@ -11,34 +11,31 @@ export default defineConfig({
|
|
11
11
|
|
12
12
|
// Configure build
|
13
13
|
build: {
|
14
|
-
outDir: 'dist/
|
14
|
+
outDir: 'dist/public',
|
15
15
|
emptyOutDir: true,
|
16
|
-
minify: process.env.NODE_ENV === 'production',
|
17
16
|
rollupOptions: {
|
18
17
|
input: {
|
19
|
-
client: resolve(__dirname, 'src/client.
|
18
|
+
client: resolve(__dirname, 'src/client.ts')
|
20
19
|
},
|
21
20
|
output: {
|
22
|
-
entryFileNames: 'assets/[name]
|
21
|
+
entryFileNames: 'assets/[name].js',
|
23
22
|
chunkFileNames: 'assets/[name]-[hash].js',
|
24
23
|
assetFileNames: 'assets/[name]-[hash].[ext]'
|
25
24
|
}
|
26
25
|
}
|
27
26
|
},
|
28
27
|
|
29
|
-
//
|
30
|
-
|
31
|
-
|
28
|
+
// Resolve aliases for better imports
|
29
|
+
resolve: {
|
30
|
+
alias: {
|
31
|
+
'@': resolve(__dirname, 'src')
|
32
|
+
}
|
32
33
|
},
|
33
34
|
|
34
35
|
// Development server
|
35
36
|
server: {
|
36
37
|
proxy: {
|
37
|
-
|
38
|
-
'/api': {
|
39
|
-
target: 'http://localhost:3000',
|
40
|
-
changeOrigin: true
|
41
|
-
}
|
38
|
+
'/api': 'http://localhost:3000'
|
42
39
|
}
|
43
40
|
}
|
44
41
|
});
|