baraqex 1.0.23 → 1.0.25
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/baraqex.js +760 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -1
- package/dist/index.js.map +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +120 -6
- package/dist/server/index.js.map +1 -1
- package/dist/server/middleware.js +1 -1
- package/dist/server/middleware.js.map +1 -1
- package/dist/server/types.d.ts +31 -3
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/types.js +0 -2
- package/dist/server/types.js.map +1 -1
- package/dist/server/wasm.d.ts +2 -2
- package/dist/server/wasm.d.ts.map +1 -1
- package/dist/server-renderer.d.ts +0 -1
- package/dist/server-renderer.d.ts.map +1 -1
- package/dist/server-renderer.js +0 -12
- package/dist/server-renderer.js.map +1 -1
- package/package.json +14 -12
package/bin/baraqex.js
CHANGED
|
@@ -323,6 +323,766 @@ async function chooseTemplate() {
|
|
|
323
323
|
return answers.template;
|
|
324
324
|
}
|
|
325
325
|
|
|
326
|
+
// Template configurations
|
|
327
|
+
const templates = {
|
|
328
|
+
basic: {
|
|
329
|
+
name: 'Basic App',
|
|
330
|
+
description: 'A simple Frontend Hamroun application',
|
|
331
|
+
files: {
|
|
332
|
+
'package.json': {
|
|
333
|
+
name: 'my-app',
|
|
334
|
+
version: '1.0.0',
|
|
335
|
+
type: 'module',
|
|
336
|
+
scripts: {
|
|
337
|
+
dev: 'node server.js',
|
|
338
|
+
start: 'node server.js'
|
|
339
|
+
},
|
|
340
|
+
dependencies: {
|
|
341
|
+
'frontend-hamroun': 'latest',
|
|
342
|
+
express: '^4.18.2'
|
|
343
|
+
}
|
|
344
|
+
},
|
|
345
|
+
'server.js': `import express from 'express';
|
|
346
|
+
import { renderToString, jsx } from 'frontend-hamroun';
|
|
347
|
+
|
|
348
|
+
const app = express();
|
|
349
|
+
const port = 3000;
|
|
350
|
+
|
|
351
|
+
// Simple component
|
|
352
|
+
function App() {
|
|
353
|
+
return jsx('div', null,
|
|
354
|
+
jsx('h1', null, 'Hello Frontend Hamroun!'),
|
|
355
|
+
jsx('p', null, 'Your app is running successfully.')
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
app.get('/', async (req, res) => {
|
|
360
|
+
const html = await renderToString(jsx(App));
|
|
361
|
+
res.send(\`
|
|
362
|
+
<!DOCTYPE html>
|
|
363
|
+
<html>
|
|
364
|
+
<head><title>My App</title></head>
|
|
365
|
+
<body><div id="root">\${html}</div></body>
|
|
366
|
+
</html>
|
|
367
|
+
\`);
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
app.listen(port, () => {
|
|
371
|
+
console.log(\`Server running at http://localhost:\${port}\`);
|
|
372
|
+
});`,
|
|
373
|
+
'README.md': `# My Frontend Hamroun App
|
|
374
|
+
|
|
375
|
+
## Getting Started
|
|
376
|
+
|
|
377
|
+
\`\`\`bash
|
|
378
|
+
npm install
|
|
379
|
+
npm run dev
|
|
380
|
+
\`\`\`
|
|
381
|
+
|
|
382
|
+
Visit http://localhost:3000 to see your app.`
|
|
383
|
+
}
|
|
384
|
+
},
|
|
385
|
+
ssr: {
|
|
386
|
+
name: 'SSR Template',
|
|
387
|
+
description: 'Advanced SSR template with hooks, modern design, and interactive components',
|
|
388
|
+
files: {
|
|
389
|
+
'package.json': {
|
|
390
|
+
name: 'ssr-template',
|
|
391
|
+
version: '0.0.0',
|
|
392
|
+
type: 'module',
|
|
393
|
+
scripts: {
|
|
394
|
+
dev: 'npm run build && npm run start',
|
|
395
|
+
build: 'node esbuild.config.js',
|
|
396
|
+
'build:vite': 'npm run build:client && npm run build:server',
|
|
397
|
+
'build:client': 'cross-env BUILD_TARGET=client vite build',
|
|
398
|
+
'build:server': 'cross-env BUILD_TARGET=server vite build --ssr',
|
|
399
|
+
start: 'node dist/server.js',
|
|
400
|
+
preview: 'npm run build && npm run start',
|
|
401
|
+
clean: 'rm -rf node_modules package-lock.json dist && npm install'
|
|
402
|
+
},
|
|
403
|
+
dependencies: {
|
|
404
|
+
express: '^4.18.2',
|
|
405
|
+
'frontend-hamroun': 'latest'
|
|
406
|
+
},
|
|
407
|
+
devDependencies: {
|
|
408
|
+
'@types/express': '^4.17.17',
|
|
409
|
+
'@types/node': '^20.0.0',
|
|
410
|
+
'@types/react': '^18.0.0',
|
|
411
|
+
'cross-env': '^7.0.3',
|
|
412
|
+
esbuild: '^0.19.0',
|
|
413
|
+
typescript: '^5.0.0',
|
|
414
|
+
vite: '^5.0.0'
|
|
415
|
+
}
|
|
416
|
+
},
|
|
417
|
+
'jsx-shim.js': `export { jsx, Fragment } from 'frontend-hamroun';`,
|
|
418
|
+
'esbuild.config.js': `import { build } from 'esbuild';
|
|
419
|
+
|
|
420
|
+
// Build client
|
|
421
|
+
await build({
|
|
422
|
+
entryPoints: ['src/client.tsx'],
|
|
423
|
+
bundle: true,
|
|
424
|
+
outfile: 'dist/client.js',
|
|
425
|
+
platform: 'browser',
|
|
426
|
+
format: 'esm',
|
|
427
|
+
minify: false,
|
|
428
|
+
jsx: 'transform',
|
|
429
|
+
jsxFactory: 'jsx',
|
|
430
|
+
jsxFragment: 'Fragment',
|
|
431
|
+
inject: ['./jsx-shim.js'],
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
// Build server
|
|
435
|
+
await build({
|
|
436
|
+
entryPoints: ['src/server.ts'],
|
|
437
|
+
bundle: true,
|
|
438
|
+
outfile: 'dist/server.js',
|
|
439
|
+
platform: 'node',
|
|
440
|
+
format: 'esm',
|
|
441
|
+
external: ['frontend-hamroun'],
|
|
442
|
+
packages: 'external',
|
|
443
|
+
minify: false,
|
|
444
|
+
jsx: 'transform',
|
|
445
|
+
jsxFactory: 'jsx',
|
|
446
|
+
jsxFragment: 'Fragment',
|
|
447
|
+
inject: ['./jsx-shim.js'],
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
console.log('Build completed successfully!');`,
|
|
451
|
+
'vite.config.ts': `import { defineConfig } from 'vite';
|
|
452
|
+
import path from 'path';
|
|
453
|
+
|
|
454
|
+
export default defineConfig({
|
|
455
|
+
build: {
|
|
456
|
+
target: 'es2022',
|
|
457
|
+
rollupOptions: {
|
|
458
|
+
input: {
|
|
459
|
+
client: 'src/client.tsx',
|
|
460
|
+
server: 'src/server.ts'
|
|
461
|
+
},
|
|
462
|
+
output: {
|
|
463
|
+
format: 'es'
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
},
|
|
467
|
+
esbuild: {
|
|
468
|
+
jsx: 'transform',
|
|
469
|
+
jsxFactory: 'jsx',
|
|
470
|
+
jsxFragment: 'Fragment',
|
|
471
|
+
jsxImportSource: 'frontend-hamroun',
|
|
472
|
+
},
|
|
473
|
+
ssr: {
|
|
474
|
+
noExternal: ['frontend-hamroun']
|
|
475
|
+
},
|
|
476
|
+
optimizeDeps: {
|
|
477
|
+
include: ['frontend-hamroun'],
|
|
478
|
+
exclude: []
|
|
479
|
+
},
|
|
480
|
+
resolve: {
|
|
481
|
+
alias: {
|
|
482
|
+
'frontend-hamroun/jsx-dev-runtime': 'frontend-hamroun',
|
|
483
|
+
'frontend-hamroun/jsx-runtime': 'frontend-hamroun'
|
|
484
|
+
}
|
|
485
|
+
},
|
|
486
|
+
define: {
|
|
487
|
+
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development')
|
|
488
|
+
}
|
|
489
|
+
});`,
|
|
490
|
+
'tsconfig.json': {
|
|
491
|
+
compilerOptions: {
|
|
492
|
+
target: 'ES2022',
|
|
493
|
+
lib: ['ES2022', 'DOM', 'DOM.Iterable'],
|
|
494
|
+
allowJs: true,
|
|
495
|
+
skipLibCheck: true,
|
|
496
|
+
esModuleInterop: true,
|
|
497
|
+
allowSyntheticDefaultImports: true,
|
|
498
|
+
strict: true,
|
|
499
|
+
forceConsistentCasingInFileNames: true,
|
|
500
|
+
module: 'ESNext',
|
|
501
|
+
moduleResolution: 'node',
|
|
502
|
+
resolveJsonModule: true,
|
|
503
|
+
isolatedModules: true,
|
|
504
|
+
noEmit: true,
|
|
505
|
+
jsx: 'preserve',
|
|
506
|
+
declaration: true,
|
|
507
|
+
outDir: './dist',
|
|
508
|
+
rootDir: './src',
|
|
509
|
+
baseUrl: '.',
|
|
510
|
+
paths: {
|
|
511
|
+
'@/*': ['./src/*']
|
|
512
|
+
}
|
|
513
|
+
},
|
|
514
|
+
include: ['src/**/*'],
|
|
515
|
+
exclude: ['node_modules', 'dist']
|
|
516
|
+
},
|
|
517
|
+
'tsconfig.server.json': {
|
|
518
|
+
extends: './tsconfig.json',
|
|
519
|
+
compilerOptions: {
|
|
520
|
+
target: 'ES2022',
|
|
521
|
+
module: 'ESNext',
|
|
522
|
+
moduleResolution: 'node',
|
|
523
|
+
lib: ['ES2022'],
|
|
524
|
+
types: ['node'],
|
|
525
|
+
allowJs: true,
|
|
526
|
+
outDir: './dist',
|
|
527
|
+
rootDir: './src',
|
|
528
|
+
esModuleInterop: true,
|
|
529
|
+
allowSyntheticDefaultImports: true,
|
|
530
|
+
skipLibCheck: true
|
|
531
|
+
},
|
|
532
|
+
include: ['src/server.ts', 'src/**/*.ts'],
|
|
533
|
+
exclude: ['src/client.tsx', 'node_modules']
|
|
534
|
+
},
|
|
535
|
+
'src/App.tsx': `import { useState, useEffect, useRef, useMemo } from 'frontend-hamroun';
|
|
536
|
+
|
|
537
|
+
// Type definitions
|
|
538
|
+
interface Todo {
|
|
539
|
+
id: number;
|
|
540
|
+
text: string;
|
|
541
|
+
completed: boolean;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
interface TodoItemProps {
|
|
545
|
+
todo: Todo;
|
|
546
|
+
onToggle: (id: number) => void;
|
|
547
|
+
onDelete: (id: number) => void;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// Custom hooks for better code organization
|
|
551
|
+
function useTimer(initialTime = 0) {
|
|
552
|
+
const [time, setTime] = useState(initialTime);
|
|
553
|
+
const [isRunning, setIsRunning] = useState(false);
|
|
554
|
+
const intervalRef = useRef(null);
|
|
555
|
+
|
|
556
|
+
const start = () => {
|
|
557
|
+
if (!isRunning) {
|
|
558
|
+
setIsRunning(true);
|
|
559
|
+
intervalRef.current = setInterval(() => {
|
|
560
|
+
setTime(prev => prev + 1);
|
|
561
|
+
}, 1000);
|
|
562
|
+
}
|
|
563
|
+
};
|
|
564
|
+
|
|
565
|
+
const stop = () => {
|
|
566
|
+
if (isRunning) {
|
|
567
|
+
setIsRunning(false);
|
|
568
|
+
if (intervalRef.current) {
|
|
569
|
+
clearInterval(intervalRef.current);
|
|
570
|
+
intervalRef.current = null;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
};
|
|
574
|
+
|
|
575
|
+
const reset = () => {
|
|
576
|
+
stop();
|
|
577
|
+
setTime(0);
|
|
578
|
+
};
|
|
579
|
+
|
|
580
|
+
useEffect(() => {
|
|
581
|
+
return () => {
|
|
582
|
+
if (intervalRef.current) {
|
|
583
|
+
clearInterval(intervalRef.current);
|
|
584
|
+
}
|
|
585
|
+
};
|
|
586
|
+
}, []);
|
|
587
|
+
|
|
588
|
+
const formattedTime = useMemo(() => {
|
|
589
|
+
const minutes = Math.floor(time / 60);
|
|
590
|
+
const seconds = time % 60;
|
|
591
|
+
return \`\${minutes.toString().padStart(2, '0')}:\${seconds.toString().padStart(2, '0')}\`;
|
|
592
|
+
}, [time]);
|
|
593
|
+
|
|
594
|
+
return { time, formattedTime, isRunning, start, stop, reset };
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
function useCounter(initialValue = 0, step = 1) {
|
|
598
|
+
const [count, setCount] = useState(initialValue);
|
|
599
|
+
|
|
600
|
+
const increment = () => setCount(prev => prev + step);
|
|
601
|
+
const decrement = () => setCount(prev => prev - step);
|
|
602
|
+
const reset = () => setCount(initialValue);
|
|
603
|
+
const setValue = (value: number | ((prev: number) => number)) => setCount(value);
|
|
604
|
+
|
|
605
|
+
return { count, increment, decrement, reset, setValue };
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
function useTodos() {
|
|
609
|
+
const [todos, setTodos] = useState([
|
|
610
|
+
{ id: 1, text: 'Learn Frontend Hamroun', completed: false },
|
|
611
|
+
{ id: 2, text: 'Build SSR app', completed: true },
|
|
612
|
+
{ id: 3, text: 'Deploy to production', completed: false }
|
|
613
|
+
]);
|
|
614
|
+
const [filter, setFilter] = useState('all');
|
|
615
|
+
|
|
616
|
+
const addTodo = (text: any) => {
|
|
617
|
+
const textString = String(text || '');
|
|
618
|
+
if (textString.trim()) {
|
|
619
|
+
setTodos(prev => [...prev, {
|
|
620
|
+
id: Date.now(),
|
|
621
|
+
text: textString.trim(),
|
|
622
|
+
completed: false
|
|
623
|
+
}]);
|
|
624
|
+
}
|
|
625
|
+
};
|
|
626
|
+
|
|
627
|
+
const toggleTodo = (id: number) => {
|
|
628
|
+
setTodos(prev => prev.map(todo =>
|
|
629
|
+
todo.id === id ? { ...todo, completed: !todo.completed } : todo
|
|
630
|
+
));
|
|
631
|
+
};
|
|
632
|
+
|
|
633
|
+
const deleteTodo = (id: number) => {
|
|
634
|
+
setTodos(prev => prev.filter(todo => todo.id !== id));
|
|
635
|
+
};
|
|
636
|
+
|
|
637
|
+
const filteredTodos = useMemo(() => {
|
|
638
|
+
const todoArray = Array.isArray(todos) ? todos : [];
|
|
639
|
+
|
|
640
|
+
switch (filter) {
|
|
641
|
+
case 'active':
|
|
642
|
+
return todoArray.filter(todo => !todo.completed);
|
|
643
|
+
case 'completed':
|
|
644
|
+
return todoArray.filter(todo => todo.completed);
|
|
645
|
+
default:
|
|
646
|
+
return todoArray;
|
|
647
|
+
}
|
|
648
|
+
}, [todos, filter]);
|
|
649
|
+
|
|
650
|
+
const stats = useMemo(() => {
|
|
651
|
+
const todoArray = Array.isArray(todos) ? todos : [];
|
|
652
|
+
const total = todoArray.length;
|
|
653
|
+
const completed = todoArray.filter(todo => todo.completed).length;
|
|
654
|
+
const active = total - completed;
|
|
655
|
+
return { total, completed, active };
|
|
656
|
+
}, [todos]);
|
|
657
|
+
|
|
658
|
+
return {
|
|
659
|
+
todos: filteredTodos,
|
|
660
|
+
filter,
|
|
661
|
+
setFilter,
|
|
662
|
+
addTodo,
|
|
663
|
+
toggleTodo,
|
|
664
|
+
deleteTodo,
|
|
665
|
+
stats
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
function useTheme() {
|
|
670
|
+
const [theme, setTheme] = useState('light');
|
|
671
|
+
|
|
672
|
+
const toggleTheme = () => {
|
|
673
|
+
setTheme(prev => prev === 'light' ? 'dark' : 'light');
|
|
674
|
+
};
|
|
675
|
+
|
|
676
|
+
const themeColors = useMemo(() => {
|
|
677
|
+
return theme === 'light' ? {
|
|
678
|
+
bg: '#ffffff',
|
|
679
|
+
text: '#333333',
|
|
680
|
+
primary: '#3498db',
|
|
681
|
+
secondary: '#e8f4fd',
|
|
682
|
+
border: '#e1e1e1',
|
|
683
|
+
accent: '#2ecc71'
|
|
684
|
+
} : {
|
|
685
|
+
bg: '#1a1a1a',
|
|
686
|
+
text: '#ffffff',
|
|
687
|
+
primary: '#5dade2',
|
|
688
|
+
secondary: '#2c3e50',
|
|
689
|
+
border: '#404040',
|
|
690
|
+
accent: '#58d68d'
|
|
691
|
+
};
|
|
692
|
+
}, [theme]);
|
|
693
|
+
|
|
694
|
+
return { theme, toggleTheme, colors: themeColors };
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
// Todo Item Component
|
|
698
|
+
function TodoItem({ todo, onToggle, onDelete }: TodoItemProps) {
|
|
699
|
+
return (
|
|
700
|
+
<div style={{
|
|
701
|
+
display: 'flex',
|
|
702
|
+
alignItems: 'center',
|
|
703
|
+
padding: '0.75rem',
|
|
704
|
+
border: '1px solid #e1e1e1',
|
|
705
|
+
borderRadius: '6px',
|
|
706
|
+
marginBottom: '0.5rem',
|
|
707
|
+
background: todo.completed ? '#f8f9fa' : 'white',
|
|
708
|
+
textDecoration: todo.completed ? 'line-through' : 'none',
|
|
709
|
+
opacity: todo.completed ? 0.7 : 1,
|
|
710
|
+
transition: 'all 0.2s ease'
|
|
711
|
+
}}>
|
|
712
|
+
<input
|
|
713
|
+
type="checkbox"
|
|
714
|
+
checked={todo.completed}
|
|
715
|
+
onChange={() => onToggle(todo.id)}
|
|
716
|
+
style={{ marginRight: '0.75rem' }}
|
|
717
|
+
/>
|
|
718
|
+
<span style={{ flex: 1, color: "black" }}>{todo.text}</span>
|
|
719
|
+
<button
|
|
720
|
+
onClick={() => onDelete(todo.id)}
|
|
721
|
+
style={{
|
|
722
|
+
background: '#e74c3c',
|
|
723
|
+
color: 'white',
|
|
724
|
+
border: 'none',
|
|
725
|
+
borderRadius: '4px',
|
|
726
|
+
padding: '0.25rem 0.5rem',
|
|
727
|
+
cursor: 'pointer',
|
|
728
|
+
fontSize: '0.8rem'
|
|
729
|
+
}}
|
|
730
|
+
>
|
|
731
|
+
Delete
|
|
732
|
+
</button>
|
|
733
|
+
</div>
|
|
734
|
+
);
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
// Stats Component
|
|
738
|
+
function StatsCard({ title, value, color = '#3498db' }: { title: string; value: string | number; color?: string }) {
|
|
739
|
+
return (
|
|
740
|
+
<div style={{
|
|
741
|
+
background: 'white',
|
|
742
|
+
padding: '1.5rem',
|
|
743
|
+
borderRadius: '12px',
|
|
744
|
+
boxShadow: '0 4px 12px rgba(0,0,0,0.1)',
|
|
745
|
+
textAlign: 'center',
|
|
746
|
+
border: \`3px solid \${color}\`,
|
|
747
|
+
transition: 'transform 0.2s ease, box-shadow 0.2s ease'
|
|
748
|
+
}}>
|
|
749
|
+
<h3 style={{
|
|
750
|
+
margin: '0 0 0.5rem 0',
|
|
751
|
+
color: color,
|
|
752
|
+
fontSize: '0.9rem',
|
|
753
|
+
textTransform: 'uppercase',
|
|
754
|
+
letterSpacing: '1px'
|
|
755
|
+
}}>
|
|
756
|
+
{title}
|
|
757
|
+
</h3>
|
|
758
|
+
<div style={{
|
|
759
|
+
fontSize: '2.5rem',
|
|
760
|
+
fontWeight: 'bold',
|
|
761
|
+
color: color,
|
|
762
|
+
fontFamily: 'monospace'
|
|
763
|
+
}}>
|
|
764
|
+
{value}
|
|
765
|
+
</div>
|
|
766
|
+
</div>
|
|
767
|
+
);
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
// Main App Component
|
|
771
|
+
export function App() {
|
|
772
|
+
const { count, increment, decrement, reset, setValue } = useCounter(0, 1);
|
|
773
|
+
const { time, formattedTime, isRunning, start, stop, reset: resetTimer } = useTimer();
|
|
774
|
+
const { todos, filter, setFilter, addTodo, toggleTodo, deleteTodo, stats } = useTodos();
|
|
775
|
+
const { theme, toggleTheme, colors } = useTheme();
|
|
776
|
+
|
|
777
|
+
const [newTodo, setNewTodo] = useState('');
|
|
778
|
+
const [showAdvanced, setShowAdvanced] = useState(false);
|
|
779
|
+
const inputRef = useRef(null);
|
|
780
|
+
|
|
781
|
+
const handleTodoSubmit = (e: any) => {
|
|
782
|
+
e.preventDefault();
|
|
783
|
+
const todoText = String(newTodo || '');
|
|
784
|
+
addTodo(todoText);
|
|
785
|
+
setNewTodo('');
|
|
786
|
+
if (inputRef.current) {
|
|
787
|
+
inputRef.current.focus();
|
|
788
|
+
}
|
|
789
|
+
};
|
|
790
|
+
|
|
791
|
+
const [step, setStep] = useState(1);
|
|
792
|
+
|
|
793
|
+
useEffect(() => {
|
|
794
|
+
console.log('App mounted - Frontend Hamroun SSR working!');
|
|
795
|
+
return () => console.log('App unmounted');
|
|
796
|
+
}, []);
|
|
797
|
+
|
|
798
|
+
return (
|
|
799
|
+
<div style={{
|
|
800
|
+
minHeight: '100vh',
|
|
801
|
+
background: \`linear-gradient(135deg, \${colors.primary} 0%, \${colors.accent} 100%)\`,
|
|
802
|
+
padding: '2rem',
|
|
803
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
804
|
+
color: colors.text,
|
|
805
|
+
transition: 'all 0.3s ease'
|
|
806
|
+
}}>
|
|
807
|
+
<div style={{
|
|
808
|
+
maxWidth: '1200px',
|
|
809
|
+
margin: '0 auto',
|
|
810
|
+
background: colors.bg,
|
|
811
|
+
borderRadius: '20px',
|
|
812
|
+
padding: '2rem',
|
|
813
|
+
boxShadow: '0 20px 40px rgba(0,0,0,0.1)',
|
|
814
|
+
border: \`1px solid \${colors.border}\`
|
|
815
|
+
}}>
|
|
816
|
+
{/* Header */}
|
|
817
|
+
<div style={{
|
|
818
|
+
textAlign: 'center',
|
|
819
|
+
marginBottom: '3rem',
|
|
820
|
+
position: 'relative'
|
|
821
|
+
}}>
|
|
822
|
+
<h1 style={{
|
|
823
|
+
fontSize: '3rem',
|
|
824
|
+
fontWeight: 'bold',
|
|
825
|
+
margin: '0 0 0.5rem 0',
|
|
826
|
+
background: \`linear-gradient(45deg, \${colors.primary}, \${colors.accent})\`,
|
|
827
|
+
WebkitBackgroundClip: 'text',
|
|
828
|
+
WebkitTextFillColor: 'transparent',
|
|
829
|
+
backgroundClip: 'text'
|
|
830
|
+
}}>
|
|
831
|
+
🚀 Frontend Hamroun SSR
|
|
832
|
+
</h1>
|
|
833
|
+
<p style={{
|
|
834
|
+
fontSize: '1.2rem',
|
|
835
|
+
color: colors.text,
|
|
836
|
+
opacity: 0.8,
|
|
837
|
+
margin: 0
|
|
838
|
+
}}>
|
|
839
|
+
Advanced SSR Template with Hooks & Modern Design
|
|
840
|
+
</p>
|
|
841
|
+
|
|
842
|
+
<button
|
|
843
|
+
onClick={toggleTheme}
|
|
844
|
+
style={{
|
|
845
|
+
position: 'absolute',
|
|
846
|
+
top: '0',
|
|
847
|
+
right: '0',
|
|
848
|
+
background: colors.secondary,
|
|
849
|
+
border: \`2px solid \${colors.border}\`,
|
|
850
|
+
borderRadius: '50px',
|
|
851
|
+
padding: '0.5rem 1rem',
|
|
852
|
+
cursor: 'pointer',
|
|
853
|
+
fontSize: '0.9rem',
|
|
854
|
+
color: colors.text,
|
|
855
|
+
transition: 'all 0.2s ease'
|
|
856
|
+
}}
|
|
857
|
+
>
|
|
858
|
+
{theme === 'light' ? '🌙' : '☀️'} {theme === 'light' ? 'Dark' : 'Light'}
|
|
859
|
+
</button>
|
|
860
|
+
</div>
|
|
861
|
+
|
|
862
|
+
{/* Stats Overview */}
|
|
863
|
+
<div style={{
|
|
864
|
+
display: 'grid',
|
|
865
|
+
gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))',
|
|
866
|
+
gap: '1.5rem',
|
|
867
|
+
marginBottom: '3rem'
|
|
868
|
+
}}>
|
|
869
|
+
<StatsCard title="Counter" value={count} color={colors.primary} />
|
|
870
|
+
<StatsCard title="Timer" value={formattedTime} color={colors.accent} />
|
|
871
|
+
<StatsCard title="Total Todos" value={stats.total} color="#f39c12" />
|
|
872
|
+
<StatsCard title="Completed" value={stats.completed} color="#27ae60" />
|
|
873
|
+
</div>
|
|
874
|
+
|
|
875
|
+
{/* Rest of the component... */}
|
|
876
|
+
</div>
|
|
877
|
+
</div>
|
|
878
|
+
);
|
|
879
|
+
}`,
|
|
880
|
+
'src/server.ts': `import express from 'express';
|
|
881
|
+
import path from 'path';
|
|
882
|
+
import { fileURLToPath } from 'url';
|
|
883
|
+
import { renderToString, jsx } from 'frontend-hamroun';
|
|
884
|
+
import { App } from './App.js';
|
|
885
|
+
|
|
886
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
887
|
+
const __dirname = path.dirname(__filename);
|
|
888
|
+
|
|
889
|
+
const app = express();
|
|
890
|
+
const port = 3000;
|
|
891
|
+
|
|
892
|
+
app.use(express.static(path.join(__dirname)));
|
|
893
|
+
|
|
894
|
+
app.get('/', async (req, res) => {
|
|
895
|
+
try {
|
|
896
|
+
const appElement = jsx(App, {});
|
|
897
|
+
const html = await renderToString(appElement);
|
|
898
|
+
|
|
899
|
+
res.send(\`
|
|
900
|
+
<!DOCTYPE html>
|
|
901
|
+
<html>
|
|
902
|
+
<head>
|
|
903
|
+
<title>Frontend Hamroun SSR - Advanced Template</title>
|
|
904
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
905
|
+
<meta name="description" content="Advanced SSR template with hooks, modern design, and interactive components">
|
|
906
|
+
</head>
|
|
907
|
+
<body>
|
|
908
|
+
<div id="root">\${html}</div>
|
|
909
|
+
<script type="module" src="/client.js"></script>
|
|
910
|
+
</body>
|
|
911
|
+
</html>
|
|
912
|
+
\`);
|
|
913
|
+
} catch (error) {
|
|
914
|
+
console.error('Server-side rendering error:', error);
|
|
915
|
+
res.status(500).send('Error rendering page');
|
|
916
|
+
}
|
|
917
|
+
});
|
|
918
|
+
|
|
919
|
+
app.listen(port, () => {
|
|
920
|
+
console.log(\`Server running at http://localhost:\${port}\`);
|
|
921
|
+
});`,
|
|
922
|
+
'src/client.tsx': `import { hydrate, jsx } from 'frontend-hamroun';
|
|
923
|
+
import { App } from './App.js';
|
|
924
|
+
|
|
925
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
926
|
+
const root = document.getElementById('root');
|
|
927
|
+
if (root) {
|
|
928
|
+
try {
|
|
929
|
+
const appElement = jsx(App, {});
|
|
930
|
+
hydrate(appElement, root);
|
|
931
|
+
console.log('✅ Client hydrated successfully!');
|
|
932
|
+
} catch (error) {
|
|
933
|
+
console.error('❌ Hydration failed:', error);
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
});`,
|
|
937
|
+
'README.md': `# Frontend Hamroun SSR Template
|
|
938
|
+
|
|
939
|
+
🚀 Advanced SSR template with hooks, modern design, and interactive components.
|
|
940
|
+
|
|
941
|
+
## Features
|
|
942
|
+
|
|
943
|
+
- ✅ Server-Side Rendering (SSR)
|
|
944
|
+
- ✅ Client-Side Hydration
|
|
945
|
+
- ✅ React-like Hooks (useState, useEffect, useRef, useMemo)
|
|
946
|
+
- ✅ Modern TypeScript Support
|
|
947
|
+
- ✅ Interactive Components (Counter, Timer, Todo List)
|
|
948
|
+
- ✅ Theme Switching (Light/Dark)
|
|
949
|
+
- ✅ Responsive Design
|
|
950
|
+
- ✅ Performance Optimized
|
|
951
|
+
|
|
952
|
+
## Getting Started
|
|
953
|
+
|
|
954
|
+
\`\`\`bash
|
|
955
|
+
# Install dependencies
|
|
956
|
+
npm install
|
|
957
|
+
|
|
958
|
+
# Build the application
|
|
959
|
+
npm run build
|
|
960
|
+
|
|
961
|
+
# Start the development server
|
|
962
|
+
npm run dev
|
|
963
|
+
\`\`\`
|
|
964
|
+
|
|
965
|
+
Visit http://localhost:3000 to see your app.
|
|
966
|
+
|
|
967
|
+
## Project Structure
|
|
968
|
+
|
|
969
|
+
\`\`\`
|
|
970
|
+
src/
|
|
971
|
+
├── App.tsx # Main application component
|
|
972
|
+
├── server.ts # Express server with SSR
|
|
973
|
+
└── client.tsx # Client-side hydration
|
|
974
|
+
dist/ # Built files
|
|
975
|
+
esbuild.config.js # Build configuration
|
|
976
|
+
\`\`\`
|
|
977
|
+
|
|
978
|
+
## Available Scripts
|
|
979
|
+
|
|
980
|
+
- \`npm run dev\` - Build and start development server
|
|
981
|
+
- \`npm run build\` - Build for production
|
|
982
|
+
- \`npm run start\` - Start the server
|
|
983
|
+
- \`npm run clean\` - Clean and reinstall dependencies
|
|
984
|
+
|
|
985
|
+
## Custom Hooks
|
|
986
|
+
|
|
987
|
+
The template includes several custom hooks:
|
|
988
|
+
|
|
989
|
+
- \`useTimer\` - Timer/stopwatch functionality
|
|
990
|
+
- \`useCounter\` - Counter with step controls
|
|
991
|
+
- \`useTodos\` - Todo list management
|
|
992
|
+
- \`useTheme\` - Theme switching
|
|
993
|
+
|
|
994
|
+
## Learn More
|
|
995
|
+
|
|
996
|
+
- [Frontend Hamroun Documentation](https://github.com/hamroun96/frontend-hamroun)
|
|
997
|
+
- [TypeScript Documentation](https://www.typescriptlang.org/)
|
|
998
|
+
- [Express.js Documentation](https://expressjs.com/)
|
|
999
|
+
`
|
|
1000
|
+
}
|
|
1001
|
+
},
|
|
1002
|
+
fullstack: {
|
|
1003
|
+
name: 'Full-Stack App',
|
|
1004
|
+
description: 'Complete full-stack application with API routes, database, and authentication',
|
|
1005
|
+
files: {
|
|
1006
|
+
'package.json': {
|
|
1007
|
+
name: 'fullstack-app',
|
|
1008
|
+
version: '1.0.0',
|
|
1009
|
+
type: 'module',
|
|
1010
|
+
scripts: {
|
|
1011
|
+
dev: 'node server.js',
|
|
1012
|
+
start: 'node server.js',
|
|
1013
|
+
build: 'echo "No build step required"'
|
|
1014
|
+
},
|
|
1015
|
+
dependencies: {
|
|
1016
|
+
'frontend-hamroun': 'latest',
|
|
1017
|
+
baraqex: 'latest'
|
|
1018
|
+
}
|
|
1019
|
+
},
|
|
1020
|
+
'server.js': `import { createDevServer } from 'baraqex/server';
|
|
1021
|
+
|
|
1022
|
+
const server = createDevServer({
|
|
1023
|
+
port: 3000,
|
|
1024
|
+
enableCors: true
|
|
1025
|
+
});
|
|
1026
|
+
|
|
1027
|
+
// Enable SSR with hydration
|
|
1028
|
+
server.enableSSR({ hydratable: true });
|
|
1029
|
+
|
|
1030
|
+
// Start the server
|
|
1031
|
+
server.start().then(() => {
|
|
1032
|
+
console.log('Full-stack server started!');
|
|
1033
|
+
});`,
|
|
1034
|
+
'src/pages/index.js': `import { jsx, useState } from 'frontend-hamroun';
|
|
1035
|
+
|
|
1036
|
+
export default function HomePage(props) {
|
|
1037
|
+
const [count, setCount] = useState(0);
|
|
1038
|
+
|
|
1039
|
+
return jsx('div', { style: { padding: '2rem', textAlign: 'center' } },
|
|
1040
|
+
jsx('h1', null, 'Welcome to Your Full-Stack App!'),
|
|
1041
|
+
jsx('p', null, \`Counter: \${count}\`),
|
|
1042
|
+
jsx('button', {
|
|
1043
|
+
onclick: () => setCount(count + 1),
|
|
1044
|
+
style: { padding: '0.5rem 1rem', margin: '0.5rem' }
|
|
1045
|
+
}, 'Increment'),
|
|
1046
|
+
jsx('button', {
|
|
1047
|
+
onclick: () => setCount(count - 1),
|
|
1048
|
+
style: { padding: '0.5rem 1rem', margin: '0.5rem' }
|
|
1049
|
+
}, 'Decrement')
|
|
1050
|
+
);
|
|
1051
|
+
}`,
|
|
1052
|
+
'src/api/hello.js': `export function get(req, res) {
|
|
1053
|
+
res.json({ message: 'Hello from API!' });
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
export function post(req, res) {
|
|
1057
|
+
const { name } = req.body;
|
|
1058
|
+
res.json({ message: \`Hello \${name || 'World'}!\` });
|
|
1059
|
+
}`,
|
|
1060
|
+
'README.md': `# Full-Stack Frontend Hamroun App
|
|
1061
|
+
|
|
1062
|
+
A complete full-stack application with API routes and SSR.
|
|
1063
|
+
|
|
1064
|
+
## Features
|
|
1065
|
+
|
|
1066
|
+
- Server-side rendering
|
|
1067
|
+
- API routes in \`src/api/\`
|
|
1068
|
+
- Page-based routing in \`src/pages/\`
|
|
1069
|
+
- Client-side hydration
|
|
1070
|
+
|
|
1071
|
+
## Getting Started
|
|
1072
|
+
|
|
1073
|
+
\`\`\`bash
|
|
1074
|
+
npm install
|
|
1075
|
+
npm run dev
|
|
1076
|
+
\`\`\`
|
|
1077
|
+
|
|
1078
|
+
Visit:
|
|
1079
|
+
- http://localhost:3000 - Homepage
|
|
1080
|
+
- http://localhost:3000/api/hello - API endpoint
|
|
1081
|
+
`
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
};
|
|
1085
|
+
|
|
326
1086
|
// Create default templates if they don't exist
|
|
327
1087
|
async function createDefaultTemplates(templatesPath) {
|
|
328
1088
|
// Templates are now created as actual directories and files
|