frontend-hamroun 1.2.0 → 1.2.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "frontend-hamroun",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "description": "A lightweight frontend and backend framework for building modern web applications",
5
5
  "type": "module",
6
6
  "main": "dist/frontend-hamroun.umd.js",
@@ -22,11 +22,12 @@
22
22
  }
23
23
  },
24
24
  "bin": {
25
- "hamroun": "src/cli/index.js"
25
+ "hamroun": "./dist/cli/index.js",
26
+ "frontend-hamroun": "./dist/cli/index.js"
26
27
  },
27
28
  "scripts": {
28
29
  "dev": "vite",
29
- "build": "vite build && tsc",
30
+ "build": "vite build && tsc && node scripts/build-cli.js",
30
31
  "test": "jest",
31
32
  "clean": "rimraf dist",
32
33
  "prebuild": "npm run clean",
@@ -0,0 +1,81 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ import { exec } from 'child_process';
5
+
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = path.dirname(__filename);
8
+ const rootDir = path.resolve(__dirname, '..');
9
+ const cliDistDir = path.resolve(rootDir, 'dist/cli');
10
+
11
+ // Ensure CLI dist directory exists
12
+ if (!fs.existsSync(cliDistDir)) {
13
+ fs.mkdirSync(cliDistDir, { recursive: true });
14
+ }
15
+
16
+ // Create CLI index.js in dist
17
+ const cliIndexPath = path.resolve(cliDistDir, 'index.js');
18
+ fs.writeFileSync(cliIndexPath, `#!/usr/bin/env node
19
+
20
+ import { fileURLToPath } from 'url';
21
+ import path from 'path';
22
+ import { createRequire } from 'module';
23
+
24
+ const __filename = fileURLToPath(import.meta.url);
25
+ const __dirname = path.dirname(__filename);
26
+ const require = createRequire(import.meta.url);
27
+
28
+ // Re-export the CLI from the compiled TypeScript
29
+ import('../cli/index.js').catch(err => {
30
+ console.error('Failed to load CLI:', err);
31
+ process.exit(1);
32
+ });
33
+ `, 'utf8');
34
+
35
+ // Make the CLI executable
36
+ try {
37
+ fs.chmodSync(cliIndexPath, '755');
38
+ console.log('CLI built successfully: dist/cli/index.js');
39
+ } catch (error) {
40
+ console.warn('Could not make CLI executable:', error);
41
+ }
42
+
43
+ // Create template directories in dist if missing
44
+ const templateSrcDir = path.resolve(rootDir, 'templates');
45
+ const templateDistDir = path.resolve(rootDir, 'dist/templates');
46
+
47
+ // Copy templates recursively
48
+ function copyTemplates() {
49
+ try {
50
+ if (!fs.existsSync(templateDistDir)) {
51
+ fs.mkdirSync(templateDistDir, { recursive: true });
52
+ }
53
+
54
+ // Copy each template
55
+ const templates = fs.readdirSync(templateSrcDir);
56
+
57
+ for (const template of templates) {
58
+ const srcTemplatePath = path.join(templateSrcDir, template);
59
+ const distTemplatePath = path.join(templateDistDir, template);
60
+
61
+ if (fs.statSync(srcTemplatePath).isDirectory()) {
62
+ if (!fs.existsSync(distTemplatePath)) {
63
+ fs.mkdirSync(distTemplatePath, { recursive: true });
64
+ }
65
+
66
+ // Use cp -r for recursively copying
67
+ exec(`cp -r ${srcTemplatePath}/* ${distTemplatePath}`, (error) => {
68
+ if (error) {
69
+ console.error(`Error copying template ${template}:`, error);
70
+ } else {
71
+ console.log(`Copied template ${template} to dist`);
72
+ }
73
+ });
74
+ }
75
+ }
76
+ } catch (error) {
77
+ console.error('Error copying templates:', error);
78
+ }
79
+ }
80
+
81
+ copyTemplates();
package/src/cli/index.ts CHANGED
@@ -0,0 +1,251 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from 'fs';
4
+ import path from 'path';
5
+ import { fileURLToPath } from 'url';
6
+ import { execSync } from 'child_process';
7
+ import readline from 'readline';
8
+
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = path.dirname(__filename);
11
+
12
+ // Get package.json to read version
13
+ const packageJsonPath = path.resolve(__dirname, '../../package.json');
14
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
15
+
16
+ // Available templates
17
+ const TEMPLATES = {
18
+ 'basic': 'Basic frontend only template',
19
+ 'full-stack': 'Complete frontend and backend template',
20
+ 'api-only': 'Backend API only template'
21
+ };
22
+
23
+ // CLI colors
24
+ const colors = {
25
+ reset: '\x1b[0m',
26
+ bright: '\x1b[1m',
27
+ green: '\x1b[32m',
28
+ blue: '\x1b[34m',
29
+ red: '\x1b[31m',
30
+ yellow: '\x1b[33m'
31
+ };
32
+
33
+ // Create readline interface
34
+ function createInterface() {
35
+ return readline.createInterface({
36
+ input: process.stdin,
37
+ output: process.stdout
38
+ });
39
+ }
40
+
41
+ // Print banner
42
+ function printBanner() {
43
+ console.log(`
44
+ ${colors.blue}${colors.bright}╔══════════════════════════════════════════════╗
45
+ ║ ║
46
+ ║ Frontend Hamroun v${packageJson.version.padEnd(25)}║
47
+ ║ A lightweight frontend & backend framework ║
48
+ ║ ║
49
+ ╚══════════════════════════════════════════════╝${colors.reset}
50
+ `);
51
+ }
52
+
53
+ // Print help
54
+ function printHelp() {
55
+ console.log(`
56
+ ${colors.bright}USAGE:${colors.reset}
57
+ ${colors.blue}npx frontend-hamroun${colors.reset} [command] [options]
58
+
59
+ ${colors.bright}COMMANDS:${colors.reset}
60
+ ${colors.blue}create${colors.reset} <project-name> [options] Create a new project
61
+ ${colors.blue}help${colors.reset} Display this help message
62
+ ${colors.blue}version${colors.reset} Display version information
63
+
64
+ ${colors.bright}OPTIONS:${colors.reset}
65
+ ${colors.blue}--template${colors.reset}, ${colors.blue}-t${colors.reset} <template> Specify template (default: basic)
66
+ ${colors.blue}--skip-install${colors.reset}, ${colors.blue}-s${colors.reset} Skip package installation
67
+
68
+ ${colors.bright}AVAILABLE TEMPLATES:${colors.reset}
69
+ ${Object.entries(TEMPLATES).map(([name, desc]) => ` ${colors.blue}${name.padEnd(12)}${colors.reset} ${desc}`).join('\n')}
70
+
71
+ ${colors.bright}EXAMPLES:${colors.reset}
72
+ ${colors.blue}npx frontend-hamroun create${colors.reset} my-app
73
+ ${colors.blue}npx frontend-hamroun create${colors.reset} my-app --template full-stack
74
+ ${colors.blue}npx frontend-hamroun create${colors.reset} my-api -t api-only --skip-install
75
+ `);
76
+ }
77
+
78
+ // Create project from template
79
+ async function createProject(projectName, options) {
80
+ const template = options.template || 'basic';
81
+
82
+ // Check if template exists
83
+ if (!Object.keys(TEMPLATES).includes(template)) {
84
+ console.error(`${colors.red}error:${colors.reset} Template "${template}" not found.`);
85
+ console.log(`Available templates: ${Object.keys(TEMPLATES).join(', ')}`);
86
+ process.exit(1);
87
+ }
88
+
89
+ // Check if project directory already exists
90
+ const projectPath = path.resolve(process.cwd(), projectName);
91
+ if (fs.existsSync(projectPath)) {
92
+ console.error(`${colors.red}error:${colors.reset} Directory ${projectName} already exists.`);
93
+ process.exit(1);
94
+ }
95
+
96
+ console.log(`
97
+ ${colors.bright}Creating a new project with Frontend Hamroun...${colors.reset}
98
+ ${colors.blue}• Project name:${colors.reset} ${projectName}
99
+ ${colors.blue}• Template:${colors.reset} ${template}
100
+ ${colors.blue}• Directory:${colors.reset} ${projectPath}
101
+ `);
102
+
103
+ try {
104
+ // Find templates directory
105
+ const templateDir = path.resolve(__dirname, '../../templates', template);
106
+
107
+ // Create project directory
108
+ fs.mkdirSync(projectPath, { recursive: true });
109
+
110
+ // Copy template files
111
+ copyTemplateFiles(templateDir, projectPath);
112
+
113
+ // Update package.json with project name
114
+ const projectPackageJsonPath = path.join(projectPath, 'package.json');
115
+ if (fs.existsSync(projectPackageJsonPath)) {
116
+ const projectPackageJson = JSON.parse(fs.readFileSync(projectPackageJsonPath, 'utf8'));
117
+ projectPackageJson.name = projectName;
118
+ fs.writeFileSync(
119
+ projectPackageJsonPath,
120
+ JSON.stringify(projectPackageJson, null, 2)
121
+ );
122
+ }
123
+
124
+ // Install dependencies
125
+ if (!options.skipInstall) {
126
+ console.log(`\n${colors.blue}Installing dependencies...${colors.reset}`);
127
+
128
+ const command = getPackageManager() === 'yarn'
129
+ ? 'yarn'
130
+ : 'npm install';
131
+
132
+ execSync(command, {
133
+ cwd: projectPath,
134
+ stdio: 'inherit'
135
+ });
136
+ }
137
+
138
+ // Success message
139
+ console.log(`
140
+ ${colors.green}${colors.bright}Success!${colors.reset} Created ${projectName} at ${projectPath}
141
+
142
+ ${colors.blue}Inside that directory, you can run several commands:${colors.reset}
143
+
144
+ ${colors.bright}npm run dev${colors.reset}
145
+ Starts the development server.
146
+
147
+ ${colors.bright}npm run build${colors.reset}
148
+ Builds the app for production.
149
+
150
+ ${colors.bright}npm start${colors.reset}
151
+ Runs the built app in production mode.
152
+
153
+ ${colors.blue}We suggest that you begin by typing:${colors.reset}
154
+
155
+ ${colors.bright}cd${colors.reset} ${projectName}
156
+ ${colors.bright}npm run dev${colors.reset}
157
+
158
+ ${colors.green}Happy coding!${colors.reset}
159
+ `);
160
+
161
+ } catch (error) {
162
+ console.error(`${colors.red}Failed to create project:${colors.reset}`, error);
163
+ process.exit(1);
164
+ }
165
+ }
166
+
167
+ // Copy template files recursively
168
+ function copyTemplateFiles(source, destination) {
169
+ const files = fs.readdirSync(source);
170
+
171
+ for (const file of files) {
172
+ const sourcePath = path.join(source, file);
173
+ const destPath = path.join(destination, file);
174
+
175
+ const stats = fs.statSync(sourcePath);
176
+
177
+ if (stats.isDirectory()) {
178
+ fs.mkdirSync(destPath, { recursive: true });
179
+ copyTemplateFiles(sourcePath, destPath);
180
+ } else {
181
+ fs.copyFileSync(sourcePath, destPath);
182
+ }
183
+ }
184
+
185
+ console.log(`${colors.green}•${colors.reset} Copied template files`);
186
+ }
187
+
188
+ // Detect package manager
189
+ function getPackageManager() {
190
+ try {
191
+ const userAgent = process.env.npm_config_user_agent;
192
+ if (userAgent && userAgent.startsWith('yarn')) {
193
+ return 'yarn';
194
+ }
195
+ return 'npm';
196
+ } catch (error) {
197
+ return 'npm';
198
+ }
199
+ }
200
+
201
+ // Parse command line arguments
202
+ function parseArgs() {
203
+ const args = process.argv.slice(2);
204
+ const command = args[0];
205
+
206
+ if (!command || command === 'help') {
207
+ printBanner();
208
+ printHelp();
209
+ process.exit(0);
210
+ }
211
+
212
+ if (command === 'version') {
213
+ console.log(`frontend-hamroun v${packageJson.version}`);
214
+ process.exit(0);
215
+ }
216
+
217
+ if (command === 'create') {
218
+ const projectName = args[1];
219
+
220
+ if (!projectName) {
221
+ console.error(`${colors.red}error:${colors.reset} Project name is required.`);
222
+ console.log(`Run ${colors.blue}npx frontend-hamroun help${colors.reset} for usage information.`);
223
+ process.exit(1);
224
+ }
225
+
226
+ // Parse options
227
+ const options = {
228
+ template: 'basic',
229
+ skipInstall: false
230
+ };
231
+
232
+ for (let i = 2; i < args.length; i++) {
233
+ if (args[i] === '--template' || args[i] === '-t') {
234
+ options.template = args[++i];
235
+ } else if (args[i] === '--skip-install' || args[i] === '-s') {
236
+ options.skipInstall = true;
237
+ }
238
+ }
239
+
240
+ printBanner();
241
+ createProject(projectName, options);
242
+ return;
243
+ }
244
+
245
+ console.error(`${colors.red}error:${colors.reset} Unknown command: ${command}`);
246
+ console.log(`Run ${colors.blue}npx frontend-hamroun help${colors.reset} for usage information.`);
247
+ process.exit(1);
248
+ }
249
+
250
+ // Run CLI
251
+ parseArgs();
@@ -0,0 +1,13 @@
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 App</title>
7
+ <link rel="stylesheet" href="/src/main.css">
8
+ </head>
9
+ <body>
10
+ <div id="app"></div>
11
+ <script type="module" src="/src/main.ts"></script>
12
+ </body>
13
+ </html>
@@ -1,43 +1,50 @@
1
1
  {
2
- "name": "{{projectName}}",
2
+ "name": "frontend-hamroun-app",
3
+ "private": true,
3
4
  "version": "0.1.0",
4
5
  "description": "{{description}}",
5
6
  "author": "{{author}}",
6
7
  "license": "MIT",
7
8
  "type": "module",
8
9
  "scripts": {
9
- "dev": "concurrently \"npm run dev:server\" \"npm run dev:client\"",
10
- "dev:server": "nodemon --watch src/server --exec \"ts-node src/server/index.ts\"",
11
- "dev:client": "vite",
12
- "build": "vite build && tsc --project tsconfig.server.json",
13
- "start": "node dist/server/index.js",
14
- "test": "jest"
10
+ "dev": "vite",
11
+ "build": "tsc && vite build",
12
+ "preview": "vite preview",
13
+ "start": "node dist/server.js",
14
+ "dev:server": "ts-node-esm --transpile-only src/server.ts",
15
+ "dev:full": "concurrently \"npm run dev\" \"npm run dev:server\""
15
16
  },
16
17
  "dependencies": {
18
+ "frontend-hamroun": "latest",
17
19
  "express": "^4.18.2",
18
- "frontend-hamroun": "^1.0.0",
20
+ "compression": "^1.7.4",
21
+ "helmet": "^7.0.0",
22
+ "morgan": "^1.10.0",
19
23
  "mongoose": "^7.0.0",
20
24
  "bcrypt": "^5.1.0",
21
25
  "jsonwebtoken": "^9.0.0",
22
- "compression": "^1.7.4",
23
26
  "cookie-parser": "^1.4.6",
24
- "dotenv": "^16.0.3",
25
- "helmet": "^6.0.1"
27
+ "dotenv": "^16.0.3"
26
28
  },
27
29
  "devDependencies": {
30
+ "@tailwindcss/forms": "^0.5.7",
31
+ "@tailwindcss/typography": "^0.5.10",
32
+ "@types/express": "^4.17.21",
33
+ "@types/node": "^20.10.0",
34
+ "autoprefixer": "^10.4.16",
35
+ "concurrently": "^8.2.2",
36
+ "postcss": "^8.4.31",
37
+ "tailwindcss": "^3.3.5",
38
+ "ts-node": "^10.9.1",
39
+ "typescript": "^5.3.2",
40
+ "vite": "^5.0.0",
28
41
  "@types/bcrypt": "^5.0.0",
29
42
  "@types/compression": "^1.7.2",
30
43
  "@types/cookie-parser": "^1.4.3",
31
- "@types/express": "^4.17.17",
32
44
  "@types/jest": "^29.5.0",
33
45
  "@types/jsonwebtoken": "^9.0.1",
34
- "@types/node": "^18.15.0",
35
- "concurrently": "^7.6.0",
36
46
  "jest": "^29.5.0",
37
47
  "nodemon": "^2.0.21",
38
- "ts-jest": "^29.0.5",
39
- "ts-node": "^10.9.1",
40
- "typescript": "^4.9.5",
41
- "vite": "^4.1.4"
48
+ "ts-jest": "^29.0.5"
42
49
  }
43
50
  }
@@ -0,0 +1,3 @@
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
@@ -0,0 +1,20 @@
1
+ import './main.css';
2
+ import { render } from 'frontend-hamroun';
3
+ import { App } from './App';
4
+ import { createApiClient } from './api';
5
+
6
+ // Initialize API client
7
+ const api = createApiClient({
8
+ baseUrl: '/api'
9
+ });
10
+
11
+ // Render the app into the DOM
12
+ document.addEventListener('DOMContentLoaded', () => {
13
+ const rootElement = document.getElementById('app');
14
+ if (rootElement) {
15
+ render(<App api={api} />, rootElement);
16
+ console.log('App rendered successfully');
17
+ } else {
18
+ console.error('Root element #app not found');
19
+ }
20
+ });