frontend-hamroun 1.2.80 → 1.2.83
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.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.client.cjs +1 -1
- package/dist/index.client.js +2 -2
- package/dist/index.js +11 -7
- package/dist/index.js.map +1 -1
- package/dist/{renderer-Din1y3YM.cjs → renderer-BL3gq8cW.cjs} +2 -2
- package/dist/{renderer-Din1y3YM.cjs.map → renderer-BL3gq8cW.cjs.map} +1 -1
- package/dist/{renderer-Bo9zkUZ_.js → renderer-Dyy-o05F.js} +2 -2
- package/dist/{renderer-Bo9zkUZ_.js.map → renderer-Dyy-o05F.js.map} +1 -1
- package/dist/{server-renderer-QHt45Ip2.js → server-renderer-C1WXH-zV.js} +99 -73
- package/dist/server-renderer-C1WXH-zV.js.map +1 -0
- package/dist/server-renderer-Chs-nmJm.cjs +2 -0
- package/dist/server-renderer-Chs-nmJm.cjs.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/src/App.jsx +16 -0
- package/templates/basic-app/src/client.jsx +5 -0
- package/templates/basic-app/src/components/Counter.jsx +13 -0
- package/templates/basic-app/src/jsx-shim.js +3 -0
- package/templates/basic-app/src/jsx-shim.ts +7 -0
- package/templates/basic-app/src/main.jsx +98 -0
- package/templates/basic-app/src/server.js +47 -0
- package/templates/complete-app/api/hello.js +0 -0
- package/templates/complete-app/lib/frontend-hamroun.js +182 -0
- package/templates/complete-app/package.json +18 -0
- package/templates/complete-app/pages/about.js +119 -0
- package/templates/complete-app/pages/about.jsx +0 -0
- package/templates/complete-app/pages/index.js +157 -0
- package/templates/complete-app/pages/index.jsx +0 -0
- package/templates/complete-app/pages/wasm-demo.js +290 -0
- package/templates/complete-app/pages/wasm-demo.jsx +0 -0
- package/templates/complete-app/public/client.js +89 -0
- package/templates/complete-app/public/index.html +118 -0
- package/templates/complete-app/public/styles.css +76 -0
- package/templates/complete-app/server.js +226 -0
- package/templates/complete-app/src/App.tsx +59 -0
- package/templates/complete-app/src/client.tsx +18 -0
- package/templates/complete-app/src/server.ts +218 -0
- package/templates/complete-app/tsconfig.json +22 -0
- package/templates/complete-app/tsconfig.server.json +19 -0
- package/templates/{ssr-template → complete-app}/vite.config.js +16 -5
- package/templates/complete-app/vite.config.ts +30 -0
- 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.css +874 -874
- package/templates/fullstack-app/build/main.css.map +7 -7
- package/templates/fullstack-app/build/main.js +996 -967
- package/templates/fullstack-app/build/main.js.map +7 -7
- package/templates/fullstack-app/package-lock.json +6301 -0
- package/templates/fullstack-app/public/styles.css +768 -768
- package/templates/go/example.go +154 -99
- package/templates/ssr-template/dist/client/assets/main-D-VH3xOb.js +1 -0
- package/templates/ssr-template/dist/client/index.html +23 -0
- package/templates/ssr-template/dist/client.js +951 -0
- package/templates/ssr-template/dist/server.js +739 -0
- package/templates/ssr-template/esbuild.config.js +33 -0
- package/templates/ssr-template/jsx-shim.js +1 -0
- package/templates/ssr-template/package.json +14 -8
- package/templates/ssr-template/src/App.tsx +847 -49
- package/templates/ssr-template/src/client.tsx +3 -17
- package/templates/ssr-template/src/server.ts +21 -204
- package/templates/ssr-template/tsconfig.json +9 -8
- package/templates/ssr-template/tsconfig.server.json +6 -14
- package/templates/ssr-template/vite.config.ts +19 -17
- package/templates/wasm/build-wasm.js +228 -0
- package/templates/wasm/dist/assets/index-BNqTDBdE.js +295 -0
- package/templates/wasm/dist/example.wasm +0 -0
- package/templates/wasm/dist/index.html +53 -0
- package/templates/{go-wasm-app/public/wasm → wasm/dist}/wasm_exec.js +572 -561
- 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/wasm/package-lock.json +4577 -0
- package/templates/wasm/package.json +25 -0
- package/templates/wasm/public/example.wasm +0 -0
- package/templates/wasm/public/wasm_exec.js +572 -0
- package/templates/wasm/src/App.tsx +550 -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 +38 -0
- package/templates/wasm/wasm-loader.js +103 -0
- package/dist/server-renderer-CqIpQ-od.cjs +0 -2
- package/dist/server-renderer-CqIpQ-od.cjs.map +0 -1
- package/dist/server-renderer-QHt45Ip2.js.map +0 -1
- package/templates/basic-app/bun.lock +0 -196
- package/templates/basic-app/docs/rapport_pfe.aux +0 -27
- 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/basic-app/package-lock.json +0 -4185
- package/templates/go-wasm-app/README.md +0 -38
- package/templates/go-wasm-app/babel.config.js +0 -15
- 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/package.json +0 -23
- 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 -521
- package/templates/go-wasm-app/src/App.jsx +0 -38
- package/templates/go-wasm-app/src/app.js +0 -153
- 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 -34
- package/templates/ssr-template/package-lock.json +0 -2478
- package/templates/ssr-template/public/index.html +0 -47
- package/templates/ssr-template/server.js +0 -369
- /package/templates/{ssr-template → complete-app}/client.js +0 -0
- /package/templates/{ssr-template → complete-app}/readme.md +0 -0
- /package/templates/{ssr-template → complete-app}/server.ts +0 -0
- /package/templates/{ssr-template → complete-app}/src/client.ts +0 -0
- /package/templates/{ssr-template → complete-app}/src/pages/index.tsx +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,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';
|
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 =await 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
|
-
});
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|