frontend-hamroun 1.2.20 → 1.2.22

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 CHANGED
@@ -459,48 +459,29 @@ async function generateApiRoute(name, directory) {
459
459
  }
460
460
 
461
461
  // Add a server template for Express
462
- const SERVER_TEMPLATE = `import { Server, Database, AuthService } from 'frontend-hamroun/server';
462
+ const SERVER_TEMPLATE = `// Import directly from frontend-hamroun
463
+ // The server module will be dynamically loaded in Node.js environment
464
+ import { server } from 'frontend-hamroun';
463
465
 
464
466
  async function startServer() {
465
467
  try {
468
+ // Dynamically import server components
469
+ const { Server, Database, AuthService, renderComponent } = await server.getServer();
470
+
466
471
  // Create and configure the server
467
472
  const app = new Server({
468
473
  port: process.env.PORT ? parseInt(process.env.PORT) : 3000,
469
474
  apiDir: './api',
475
+ pagesDir: './src/pages', // For SSR pages
470
476
  staticDir: './public',
471
477
 
472
478
  // Enable CORS
473
479
  enableCors: true,
474
480
  corsOptions: {
475
- origin: process.env.CORS_ORIGIN || '*', // Set to specific domain in production
481
+ origin: process.env.CORS_ORIGIN || '*',
476
482
  methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
477
483
  allowedHeaders: ['Content-Type', 'Authorization']
478
- },
479
-
480
- // Uncomment to enable database (choose one)
481
- /*
482
- db: {
483
- // MongoDB
484
- url: process.env.MONGODB_URL || 'mongodb://localhost:27017/my_app',
485
- type: 'mongodb'
486
-
487
- // MySQL
488
- // url: process.env.MYSQL_URL || 'mysql://user:password@localhost:3306/my_db',
489
- // type: 'mysql'
490
-
491
- // PostgreSQL
492
- // url: process.env.POSTGRES_URL || 'postgres://user:password@localhost:5432/my_db',
493
- // type: 'postgres'
494
- },
495
- */
496
-
497
- // Uncomment to enable authentication
498
- /*
499
- auth: {
500
- secret: process.env.JWT_SECRET || 'your-secret-key',
501
- expiresIn: '7d'
502
484
  }
503
- */
504
485
  });
505
486
 
506
487
  // Connect to database if configured
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "frontend-hamroun",
3
- "version": "1.2.20",
3
+ "version": "1.2.22",
4
4
  "description": "A lightweight full-stack JavaScript framework",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -24,6 +24,11 @@
24
24
  "types": "./dist/server/index.d.ts",
25
25
  "import": "./dist/server/index.js",
26
26
  "default": "./dist/server/index.js"
27
+ },
28
+ "./ssr": {
29
+ "types": "./dist/server-renderer.d.ts",
30
+ "import": "./dist/server-renderer.js",
31
+ "default": "./dist/server-renderer.js"
27
32
  }
28
33
  },
29
34
  "bin": {
@@ -0,0 +1,30 @@
1
+ import { hydrate } from 'frontend-hamroun';
2
+
3
+ // Dynamically import the page component that matches the current URL
4
+ async function hydratePage() {
5
+ try {
6
+ // Get the current path
7
+ const path = window.location.pathname;
8
+
9
+ // Import the corresponding page component
10
+ const pagePath = `/src/pages${path === '/' ? '/index' : path}.jsx`;
11
+
12
+ try {
13
+ const { default: PageComponent } = await import(pagePath);
14
+
15
+ // Find the root element to hydrate
16
+ const rootElement = document.getElementById('app');
17
+ if (rootElement) {
18
+ // Hydrate the server-rendered HTML with the client component
19
+ hydrate(<PageComponent />, rootElement);
20
+ }
21
+ } catch (error) {
22
+ console.error(`Error importing page component for path ${path}:`, error);
23
+ }
24
+ } catch (error) {
25
+ console.error('Error during hydration:', error);
26
+ }
27
+ }
28
+
29
+ // Start hydration when the DOM is ready
30
+ document.addEventListener('DOMContentLoaded', hydratePage);
@@ -1,14 +1,16 @@
1
1
  {
2
- "name": "ssr-version",
2
+ "name": "ssr-app",
3
3
  "private": true,
4
- "version": "0.0.0",
4
+ "version": "0.1.0",
5
5
  "type": "module",
6
6
  "scripts": {
7
- "dev": "vite",
7
+ "dev:client": "vite",
8
+ "dev:server": "node --loader ts-node/esm server.ts",
9
+ "dev": "concurrently \"npm run dev:server\" \"npm run dev:client\"",
8
10
  "build:client": "vite build",
9
- "build:server": "vite build --ssr",
11
+ "build:server": "tsc --project tsconfig.server.json",
10
12
  "build": "npm run build:client && npm run build:server",
11
- "start": "node dist/server.js"
13
+ "start": "node dist/server/server.js"
12
14
  },
13
15
  "dependencies": {
14
16
  "express": "^4.18.2",
@@ -16,8 +18,10 @@
16
18
  },
17
19
  "devDependencies": {
18
20
  "@types/express": "^4.17.17",
19
- "@types/node": "^20.5.0",
20
- "typescript": "^5.0.0",
21
+ "@types/node": "^18.16.19",
22
+ "concurrently": "^8.2.1",
23
+ "ts-node": "^10.9.1",
24
+ "typescript": "^5.2.2",
21
25
  "vite": "^4.4.9"
22
26
  }
23
27
  }
@@ -0,0 +1,26 @@
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>Frontend Hamroun SSR</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
+ </head>
13
+ <body>
14
+ <!-- App will be rendered here by SSR -->
15
+ <div id="app">
16
+ <!-- This comment will be replaced with server-rendered HTML -->
17
+ <!-- If you see this text, server-side rendering failed -->
18
+ <div class="flex items-center justify-center min-h-screen">
19
+ <div class="text-center">
20
+ <h1 class="text-xl font-bold">Loading...</h1>
21
+ <p>If this message persists, make sure your server is running.</p>
22
+ </div>
23
+ </div>
24
+ </div>
25
+ </body>
26
+ </html>
@@ -0,0 +1,46 @@
1
+ import { Server, renderComponent } from 'frontend-hamroun/server';
2
+ import { join } from 'path';
3
+ import express from 'express';
4
+
5
+ async function startServer() {
6
+ try {
7
+ // Create and configure the server
8
+ const app = new Server({
9
+ port: process.env.PORT ? parseInt(process.env.PORT) : 3000,
10
+ pagesDir: './src/pages',
11
+ staticDir: './public',
12
+
13
+ // Enable CORS for API endpoints
14
+ enableCors: true
15
+ });
16
+
17
+ // Add server-side data endpoints (optional)
18
+ const expressApp = app.getExpressApp();
19
+
20
+ // Example API endpoint that provides data to SSR pages
21
+ expressApp.get('/api/page-data', (req, res) => {
22
+ res.json({
23
+ title: 'Server-side Data',
24
+ content: 'This data was fetched from the server',
25
+ timestamp: new Date().toISOString()
26
+ });
27
+ });
28
+
29
+ // Start the server
30
+ await app.start();
31
+
32
+ console.log(`Server running at http://localhost:${process.env.PORT || 3000}`);
33
+
34
+ // Handle graceful shutdown
35
+ process.on('SIGINT', async () => {
36
+ console.log('Shutting down server...');
37
+ await app.stop();
38
+ process.exit(0);
39
+ });
40
+ } catch (error) {
41
+ console.error('Failed to start server:', error);
42
+ process.exit(1);
43
+ }
44
+ }
45
+
46
+ startServer();
@@ -1,9 +1,18 @@
1
- import { hydrate } from 'frontend-hamroun';
2
- import { App } from './App';
1
+ import { hydrate, createElement } from 'frontend-hamroun';
3
2
 
3
+ // For simplicity in this example, we just hydrate the root component
4
+ // In a more complex app, you might use a router
5
+ import HomePage from './pages/index';
6
+
7
+ // When the DOM is ready, hydrate the server-rendered HTML
4
8
  document.addEventListener('DOMContentLoaded', () => {
5
- hydrate(
6
- <App />,
7
- document.getElementById('root')!
8
- );
9
+ const rootElement = document.getElementById('app');
10
+
11
+ if (rootElement) {
12
+ // Hydrate the app with the same component that was rendered on the server
13
+ hydrate(<HomePage />, rootElement);
14
+ console.log('Hydration complete');
15
+ } else {
16
+ console.error('Could not find root element with id "app"');
17
+ }
9
18
  });
@@ -0,0 +1,51 @@
1
+ import { useState, useEffect } from 'frontend-hamroun';
2
+
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
+
8
+ // This effect only runs on the client after hydration
9
+ useEffect(() => {
10
+ // Set server render time (only when hydrating)
11
+ if (!serverTime) {
12
+ setServerTime('Client-side hydration complete');
13
+ }
14
+
15
+ // Simple cleanup function
16
+ return () => {
17
+ console.log('Component unmounting');
18
+ };
19
+ }, []);
20
+
21
+ return (
22
+ <div id="app">
23
+ <div className="hero min-h-screen bg-base-200">
24
+ <div className="hero-content text-center">
25
+ <div className="max-w-md">
26
+ <h1 className="text-5xl font-bold">Frontend Hamroun SSR</h1>
27
+ <p className="py-6">
28
+ This page was rendered on the server and hydrated on the client.
29
+ </p>
30
+
31
+ {/* Interactive counter demonstrates client-side hydration */}
32
+ <div className="my-4 p-4 bg-base-300 rounded-lg">
33
+ <p>Counter: {count}</p>
34
+ <button
35
+ className="btn btn-primary mt-2"
36
+ onClick={() => setCount(count + 1)}
37
+ >
38
+ Increment
39
+ </button>
40
+ </div>
41
+
42
+ {/* Shows server render time or hydration status */}
43
+ <div className="text-sm opacity-70 mt-4">
44
+ {serverTime ? serverTime : `Server rendered at: ${new Date().toISOString()}`}
45
+ </div>
46
+ </div>
47
+ </div>
48
+ </div>
49
+ </div>
50
+ );
51
+ }
@@ -1,25 +1,23 @@
1
1
  {
2
2
  "compilerOptions": {
3
- "target": "ES2020",
4
- "module": "ESNext",
5
- "lib": ["ES2020", "DOM", "DOM.Iterable"],
6
- "moduleResolution": "bundler",
7
- "jsx": "preserve",
8
- "jsxFactory": "jsx",
9
- "jsxFragmentFactory": "Fragment",
10
- "strict": true,
11
- "esModuleInterop": true,
3
+ "target": "ESNext",
4
+ "useDefineForClassFields": true,
5
+ "lib": ["DOM", "DOM.Iterable", "ESNext"],
6
+ "allowJs": false,
12
7
  "skipLibCheck": true,
13
- "forceConsistentCasingInFileNames": true,
8
+ "esModuleInterop": true,
14
9
  "allowSyntheticDefaultImports": true,
10
+ "strict": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "module": "ESNext",
13
+ "moduleResolution": "Node",
15
14
  "resolveJsonModule": true,
16
15
  "isolatedModules": true,
17
16
  "noEmit": true,
18
- "types": ["node"],
19
- "paths": {
20
- "frontend-hamroun": ["./node_modules/frontend-hamroun"]
21
- }
17
+ "jsx": "react",
18
+ "jsxFactory": "createElement",
19
+ "jsxFragmentFactory": "Fragment"
22
20
  },
23
21
  "include": ["src"],
24
- "exclude": ["node_modules", "dist"]
22
+ "references": [{ "path": "./tsconfig.server.json" }]
25
23
  }
@@ -1,11 +1,19 @@
1
1
  {
2
2
  "extends": "./tsconfig.json",
3
3
  "compilerOptions": {
4
- "module": "ESNext",
5
- "outDir": "dist",
6
- "noEmit": false,
7
- "jsx": "preserve",
8
- "moduleResolution": "bundler"
4
+ "target": "ES2020",
5
+ "module": "NodeNext",
6
+ "moduleResolution": "NodeNext",
7
+ "esModuleInterop": true,
8
+ "outDir": "./dist/server",
9
+ "declaration": true,
10
+ "sourceMap": true,
11
+ "strict": true,
12
+ "skipLibCheck": true,
13
+ "jsx": "react",
14
+ "jsxFactory": "createElement",
15
+ "jsxFragmentFactory": "Fragment"
9
16
  },
10
- "include": ["src/server.ts"]
17
+ "include": ["server.ts", "src/pages/**/*.tsx"],
18
+ "exclude": ["node_modules", "dist"]
11
19
  }
@@ -0,0 +1,44 @@
1
+ import { defineConfig } from 'vite';
2
+ import { resolve } from 'path';
3
+
4
+ export default defineConfig({
5
+ // Configure JSX
6
+ esbuild: {
7
+ jsxFactory: 'createElement',
8
+ jsxFragment: 'Fragment',
9
+ jsxInject: `import { createElement, Fragment } from 'frontend-hamroun'`
10
+ },
11
+
12
+ // Configure build
13
+ build: {
14
+ outDir: 'dist/client',
15
+ emptyOutDir: true,
16
+ minify: process.env.NODE_ENV === 'production',
17
+ rollupOptions: {
18
+ input: {
19
+ client: resolve(__dirname, 'src/client.tsx')
20
+ },
21
+ output: {
22
+ entryFileNames: 'assets/[name]-[hash].js',
23
+ chunkFileNames: 'assets/[name]-[hash].js',
24
+ assetFileNames: 'assets/[name]-[hash].[ext]'
25
+ }
26
+ }
27
+ },
28
+
29
+ // Optimize dependencies
30
+ optimizeDeps: {
31
+ include: ['frontend-hamroun']
32
+ },
33
+
34
+ // Development server
35
+ server: {
36
+ proxy: {
37
+ // Forward API requests to the actual Node.js server during development
38
+ '/api': {
39
+ target: 'http://localhost:3000',
40
+ changeOrigin: true
41
+ }
42
+ }
43
+ }
44
+ });