frontend-hamroun 1.2.16 → 1.2.17
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/README.md +4 -0
- package/bin/cli.js +673 -0
- package/dist/component.d.ts +1 -1
- package/dist/context.d.ts +4 -3
- package/dist/index.client.d.ts +11 -0
- package/dist/index.d.ts +9 -89
- package/dist/index.js +396 -67
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +392 -0
- package/dist/index.mjs.map +1 -0
- package/dist/jsx-runtime/jsx-runtime.d.ts +0 -1
- package/dist/jsx-runtime.d.ts +1 -1
- package/dist/renderer.d.ts +0 -10
- package/dist/server-renderer.d.ts +0 -3
- package/dist/server-types.d.ts +42 -0
- package/package.json +69 -41
- package/templates/basic-app/index.html +6 -6
- package/templates/basic-app/package.json +18 -7
- package/templates/basic-app/postcss.config.js +0 -1
- package/templates/basic-app/src/main.tsx +1 -10
- package/templates/basic-app/tailwind.config.js +2 -23
- package/templates/basic-app/tsconfig.json +4 -17
- package/templates/basic-app/vite.config.ts +3 -54
- package/templates/fullstack-app/api/hello.ts +18 -0
- package/templates/fullstack-app/api/users/[id].ts +73 -0
- package/templates/fullstack-app/api/users/index.ts +32 -0
- package/templates/fullstack-app/package.json +31 -0
- package/templates/fullstack-app/server.ts +46 -0
- package/templates/fullstack-app/src/pages/index.tsx +59 -0
- package/templates/ssr-template/vite.config.ts +1 -11
- package/bin/cli.cjs +0 -16
- package/bin/cli.mjs +0 -237
- package/dist/backend/api-utils.d.ts +0 -38
- package/dist/backend/api-utils.js +0 -135
- package/dist/backend/auth.d.ts +0 -134
- package/dist/backend/auth.js +0 -387
- package/dist/backend/database.d.ts +0 -27
- package/dist/backend/database.js +0 -91
- package/dist/backend/model.d.ts +0 -43
- package/dist/backend/model.js +0 -178
- package/dist/backend/router.d.ts +0 -27
- package/dist/backend/router.js +0 -137
- package/dist/backend/server.d.ts +0 -19
- package/dist/backend/server.js +0 -268
- package/dist/backend/types.d.ts +0 -217
- package/dist/backend/types.js +0 -1
- package/dist/batch.js +0 -22
- package/dist/cli/index.d.ts +0 -2
- package/dist/cli/index.js +0 -215
- package/dist/component.js +0 -84
- package/dist/components/Counter.js +0 -2
- package/dist/context.js +0 -18
- package/dist/frontend-hamroun.es.js +0 -1378
- package/dist/frontend-hamroun.umd.js +0 -66
- package/dist/hooks.js +0 -164
- package/dist/jsx-runtime/index.d.ts +0 -11
- package/dist/jsx-runtime/index.js +0 -19
- package/dist/jsx-runtime/jsx-dev-runtime.js +0 -1
- package/dist/jsx-runtime/jsx-runtime.js +0 -95
- package/dist/jsx-runtime.js +0 -192
- package/dist/renderer.js +0 -51
- package/dist/server-renderer.js +0 -102
- package/dist/types.js +0 -1
- package/dist/vdom.js +0 -27
- package/scripts/build-cli.js +0 -1199
- package/scripts/generate.js +0 -134
- package/src/backend/api-utils.ts +0 -178
- package/src/backend/auth.ts +0 -544
- package/src/backend/database.ts +0 -104
- package/src/backend/model.ts +0 -198
- package/src/backend/router.ts +0 -176
- package/src/backend/server.ts +0 -330
- package/src/backend/types.ts +0 -257
- package/src/batch.ts +0 -24
- package/src/cli/index.js +0 -554
- package/src/cli/index.ts +0 -257
- package/src/component.ts +0 -98
- package/src/components/Counter.tsx +0 -4
- package/src/context.ts +0 -29
- package/src/hooks.ts +0 -211
- package/src/index.ts +0 -144
- package/src/jsx-runtime/index.ts +0 -27
- package/src/jsx-runtime/jsx-dev-runtime.ts +0 -0
- package/src/jsx-runtime/jsx-runtime.ts +0 -104
- package/src/jsx-runtime.ts +0 -226
- package/src/renderer.ts +0 -55
- package/src/server-renderer.ts +0 -114
- package/src/shims.d.ts +0 -20
- package/src/types/bcrypt.d.ts +0 -30
- package/src/types/jsonwebtoken.d.ts +0 -55
- package/src/types.d.ts +0 -26
- package/src/types.ts +0 -21
- package/src/vdom.ts +0 -34
- package/templates/basic/.eslintignore +0 -5
- package/templates/basic/.eslintrc.json +0 -25
- package/templates/basic/docs/rapport_pfe.aux +0 -27
- package/templates/basic/docs/rapport_pfe.log +0 -399
- package/templates/basic/docs/rapport_pfe.out +0 -10
- package/templates/basic/docs/rapport_pfe.pdf +0 -0
- package/templates/basic/docs/rapport_pfe.tex +0 -68
- package/templates/basic/docs/rapport_pfe.toc +0 -14
- package/templates/basic/index.html +0 -12
- package/templates/basic/jsconfig.json +0 -14
- package/templates/basic/package.json +0 -18
- package/templates/basic/postcss.config.js +0 -7
- package/templates/basic/src/App.js +0 -105
- package/templates/basic/src/App.tsx +0 -65
- package/templates/basic/src/api.ts +0 -58
- package/templates/basic/src/components/Counter.tsx +0 -26
- package/templates/basic/src/components/Header.tsx +0 -9
- package/templates/basic/src/components/TodoList.tsx +0 -90
- package/templates/basic/src/main.css +0 -3
- package/templates/basic/src/main.js +0 -11
- package/templates/basic/src/main.ts +0 -20
- package/templates/basic/src/main.tsx +0 -144
- package/templates/basic/src/server.ts +0 -99
- package/templates/basic/tailwind.config.js +0 -32
- package/templates/basic/tsconfig.json +0 -20
- package/templates/basic/tsconfig.node.json +0 -10
- package/templates/basic/vite.config.js +0 -18
- package/templates/basic/vite.config.ts +0 -86
- package/templates/basic-app/src/App.js +0 -105
- package/templates/basic-app/src/App.tsx +0 -143
- package/templates/basic-app/src/api.ts +0 -58
- package/templates/basic-app/src/components/Counter.tsx +0 -26
- package/templates/basic-app/src/components/Header.tsx +0 -9
- package/templates/basic-app/src/components/TodoList.tsx +0 -90
- package/templates/basic-app/src/main.js +0 -10
- package/templates/basic-app/src/main.ts +0 -21
- package/templates/basic-app/src/react/index.ts +0 -35
- package/templates/basic-app/src/react/jsx-dev-runtime.ts +0 -13
- package/templates/basic-app/src/react/jsx-runtime.ts +0 -12
- package/templates/basic-app/src/server.ts +0 -99
- package/templates/basic-app/src/shims.ts +0 -9
- package/templates/basic-app/tsconfig.node.json +0 -10
- package/templates/basic-app/vite.config.js +0 -22
- package/templates/full-stack/.env.example +0 -11
- package/templates/full-stack/README.md +0 -51
- package/templates/full-stack/index.html +0 -12
- package/templates/full-stack/jsconfig.json +0 -14
- package/templates/full-stack/package.json +0 -21
- package/templates/full-stack/src/App.js +0 -105
- package/templates/full-stack/src/client/App.tsx +0 -50
- package/templates/full-stack/src/client/components/Header.tsx +0 -42
- package/templates/full-stack/src/client/components/UserList.tsx +0 -29
- package/templates/full-stack/src/client/main.tsx +0 -5
- package/templates/full-stack/src/main.css +0 -3
- package/templates/full-stack/src/main.js +0 -11
- package/templates/full-stack/src/main.ts +0 -20
- package/templates/full-stack/src/server/index.ts +0 -99
- package/templates/full-stack/src/server/routes/auth.ts +0 -39
- package/templates/full-stack/src/server/routes/users.ts +0 -48
- package/templates/full-stack/src/shims.ts +0 -9
- package/templates/full-stack/tsconfig.json +0 -20
- package/templates/full-stack/tsconfig.node.json +0 -10
- package/templates/full-stack/tsconfig.server.json +0 -15
- package/templates/full-stack/vite.config.js +0 -18
- package/templates/full-stack/vite.config.ts +0 -85
package/src/cli/index.js
DELETED
@@ -1,554 +0,0 @@
|
|
1
|
-
#!/usr/bin/env node
|
2
|
-
|
3
|
-
import fs from 'fs';
|
4
|
-
import path from 'path';
|
5
|
-
import readline from 'readline';
|
6
|
-
import { exec } from 'child_process';
|
7
|
-
import { promisify } from 'util';
|
8
|
-
|
9
|
-
// Promisify fs functions
|
10
|
-
const mkdir = promisify(fs.mkdir);
|
11
|
-
const readdir = promisify(fs.readdir);
|
12
|
-
const writeFile = promisify(fs.writeFile);
|
13
|
-
const readFile = promisify(fs.readFile);
|
14
|
-
const copyFile = promisify(fs.copyFile);
|
15
|
-
const access = promisify(fs.access);
|
16
|
-
const execPromise = promisify(exec);
|
17
|
-
|
18
|
-
// Find the templates directory
|
19
|
-
const TEMPLATES_DIR = path.join(path.dirname(new URL(import.meta.url).pathname), '..', 'templates');
|
20
|
-
|
21
|
-
// Create readline interface
|
22
|
-
const rl = readline.createInterface({
|
23
|
-
input: process.stdin,
|
24
|
-
output: process.stdout
|
25
|
-
});
|
26
|
-
|
27
|
-
// Promisify question
|
28
|
-
const question = (query) => new Promise((resolve) => rl.question(query, resolve));
|
29
|
-
|
30
|
-
// Simple logger
|
31
|
-
const log = {
|
32
|
-
info: (msg) => console.log(`\x1b[36minfo\x1b[0m: ${msg}`),
|
33
|
-
success: (msg) => console.log(`\x1b[32msuccess\x1b[0m: ${msg}`),
|
34
|
-
warn: (msg) => console.log(`\x1b[33mwarning\x1b[0m: ${msg}`),
|
35
|
-
error: (msg) => console.log(`\x1b[31merror\x1b[0m: ${msg}`),
|
36
|
-
title: (msg) => console.log(`\n\x1b[1m${msg}\x1b[0m\n`)
|
37
|
-
};
|
38
|
-
|
39
|
-
// Rest of the CLI functionality...
|
40
|
-
/**
|
41
|
-
* Copy directory recursively
|
42
|
-
*/
|
43
|
-
async function copyDir(src, dest) {
|
44
|
-
// Create destination directory if it doesn't exist
|
45
|
-
await mkdir(dest, { recursive: true });
|
46
|
-
|
47
|
-
// Read the contents of the source directory
|
48
|
-
const entries = await readdir(src, { withFileTypes: true });
|
49
|
-
|
50
|
-
// Process each file/directory
|
51
|
-
for (const entry of entries) {
|
52
|
-
const srcPath = path.join(src, entry.name);
|
53
|
-
const destPath = path.join(dest, entry.name);
|
54
|
-
|
55
|
-
if (entry.isDirectory()) {
|
56
|
-
// Recursively copy directories
|
57
|
-
await copyDir(srcPath, destPath);
|
58
|
-
} else {
|
59
|
-
// Copy files
|
60
|
-
await copyFile(srcPath, destPath);
|
61
|
-
}
|
62
|
-
}
|
63
|
-
}
|
64
|
-
|
65
|
-
/**
|
66
|
-
* Check if directory exists
|
67
|
-
*/
|
68
|
-
async function directoryExists(path) {
|
69
|
-
try {
|
70
|
-
await access(path, fs.constants.F_OK);
|
71
|
-
return true;
|
72
|
-
} catch (error) {
|
73
|
-
return false;
|
74
|
-
}
|
75
|
-
}
|
76
|
-
|
77
|
-
/**
|
78
|
-
* Replace placeholders in file content
|
79
|
-
*/
|
80
|
-
async function processTemplateFile(filePath, replacements) {
|
81
|
-
try {
|
82
|
-
// Read file content
|
83
|
-
let content = await readFile(filePath, 'utf8');
|
84
|
-
|
85
|
-
// Replace each placeholder
|
86
|
-
for (const [key, value] of Object.entries(replacements)) {
|
87
|
-
content = content.replace(new RegExp(`\\{\\{\\s*${key}\\s*\\}\\}`, 'g'), value);
|
88
|
-
}
|
89
|
-
|
90
|
-
// Write back to file
|
91
|
-
await writeFile(filePath, content, 'utf8');
|
92
|
-
} catch (error) {
|
93
|
-
log.error(`Failed to process template file ${filePath}: ${error.message}`);
|
94
|
-
}
|
95
|
-
}
|
96
|
-
|
97
|
-
/**
|
98
|
-
* Process all template files in a directory
|
99
|
-
*/
|
100
|
-
async function processTemplateDir(dir, replacements) {
|
101
|
-
try {
|
102
|
-
const entries = await readdir(dir, { withFileTypes: true });
|
103
|
-
|
104
|
-
for (const entry of entries) {
|
105
|
-
const fullPath = path.join(dir, entry.name);
|
106
|
-
|
107
|
-
if (entry.isDirectory()) {
|
108
|
-
await processTemplateDir(fullPath, replacements);
|
109
|
-
} else if (entry.isFile() &&
|
110
|
-
(entry.name.endsWith('.js') ||
|
111
|
-
entry.name.endsWith('.ts') ||
|
112
|
-
entry.name.endsWith('.json') ||
|
113
|
-
entry.name.endsWith('.tsx') ||
|
114
|
-
entry.name.endsWith('.md') ||
|
115
|
-
entry.name.endsWith('.html'))) {
|
116
|
-
await processTemplateFile(fullPath, replacements);
|
117
|
-
}
|
118
|
-
}
|
119
|
-
} catch (error) {
|
120
|
-
log.error(`Failed to process template directory ${dir}: ${error.message}`);
|
121
|
-
}
|
122
|
-
}
|
123
|
-
|
124
|
-
/**
|
125
|
-
* Create a new Hamroun project
|
126
|
-
*/
|
127
|
-
async function createProject(projectName, templateName) {
|
128
|
-
const targetDir = path.resolve(process.cwd(), projectName);
|
129
|
-
|
130
|
-
// Check if directory already exists
|
131
|
-
if (await directoryExists(targetDir)) {
|
132
|
-
const overwrite = await question(`Directory ${projectName} already exists. Continue and overwrite? (y/N): `);
|
133
|
-
if (overwrite.toLowerCase() !== 'y') {
|
134
|
-
log.info('Project creation cancelled.');
|
135
|
-
rl.close();
|
136
|
-
return;
|
137
|
-
}
|
138
|
-
}
|
139
|
-
|
140
|
-
// Check if template exists
|
141
|
-
const templateDir = path.join(TEMPLATES_DIR, templateName);
|
142
|
-
if (!(await directoryExists(templateDir))) {
|
143
|
-
log.error(`Template "${templateName}" not found.`);
|
144
|
-
rl.close();
|
145
|
-
return;
|
146
|
-
}
|
147
|
-
|
148
|
-
try {
|
149
|
-
log.title(`Creating new Hamroun project: ${projectName}`);
|
150
|
-
|
151
|
-
// Create project directory
|
152
|
-
await mkdir(targetDir, { recursive: true });
|
153
|
-
|
154
|
-
// Copy template files
|
155
|
-
log.info(`Copying template files from ${templateName}...`);
|
156
|
-
await copyDir(templateDir, targetDir);
|
157
|
-
|
158
|
-
// Get template customization values
|
159
|
-
const appName = await question('Application name: ') || projectName;
|
160
|
-
const description = await question('Description: ') || `A Hamroun application`;
|
161
|
-
const author = await question('Author: ') || '';
|
162
|
-
|
163
|
-
// Replace placeholders in template files
|
164
|
-
log.info('Processing template files...');
|
165
|
-
await processTemplateDir(targetDir, {
|
166
|
-
projectName,
|
167
|
-
appName,
|
168
|
-
description,
|
169
|
-
author,
|
170
|
-
creationDate: new Date().toISOString().split('T')[0]
|
171
|
-
});
|
172
|
-
|
173
|
-
// Install dependencies
|
174
|
-
log.info('Installing dependencies (this may take a while)...');
|
175
|
-
try {
|
176
|
-
await execPromise('npm install', { cwd: targetDir });
|
177
|
-
log.success('Dependencies installed successfully.');
|
178
|
-
} catch (error) {
|
179
|
-
log.warn(`Failed to install dependencies: ${error.message}`);
|
180
|
-
log.info('You can install them later by running "npm install" in the project directory.');
|
181
|
-
}
|
182
|
-
|
183
|
-
log.success(`Project ${projectName} created successfully!`);
|
184
|
-
log.info(`\nTo get started:\n`);
|
185
|
-
log.info(` cd ${projectName}`);
|
186
|
-
log.info(` npm run dev\n`);
|
187
|
-
|
188
|
-
} catch (error) {
|
189
|
-
log.error(`Failed to create project: ${error.message}`);
|
190
|
-
} finally {
|
191
|
-
rl.close();
|
192
|
-
}
|
193
|
-
}
|
194
|
-
|
195
|
-
/**
|
196
|
-
* Generate a component
|
197
|
-
*/
|
198
|
-
async function generateComponent(componentName, options = {}) {
|
199
|
-
try {
|
200
|
-
const { type = 'functional', directory = 'components' } = options;
|
201
|
-
const targetDir = path.resolve(process.cwd(), directory);
|
202
|
-
|
203
|
-
// Create directory if it doesn't exist
|
204
|
-
await mkdir(targetDir, { recursive: true });
|
205
|
-
|
206
|
-
// Determine file path and content
|
207
|
-
const componentFileName = `${componentName}.tsx`;
|
208
|
-
const componentFilePath = path.join(targetDir, componentFileName);
|
209
|
-
|
210
|
-
// Check if file already exists
|
211
|
-
if (fs.existsSync(componentFilePath)) {
|
212
|
-
const overwrite = await question(`Component ${componentName} already exists. Overwrite? (y/N): `);
|
213
|
-
if (overwrite.toLowerCase() !== 'y') {
|
214
|
-
log.info('Component generation cancelled.');
|
215
|
-
rl.close();
|
216
|
-
return;
|
217
|
-
}
|
218
|
-
}
|
219
|
-
|
220
|
-
// Generate component content based on type
|
221
|
-
let content = '';
|
222
|
-
if (type === 'functional') {
|
223
|
-
content = `import { useState, useEffect } from 'frontend-hamroun';
|
224
|
-
|
225
|
-
interface ${componentName}Props {
|
226
|
-
title?: string;
|
227
|
-
}
|
228
|
-
|
229
|
-
export function ${componentName}({ title = 'Default Title' }: ${componentName}Props) {
|
230
|
-
const [count, setCount] = useState(0);
|
231
|
-
|
232
|
-
useEffect(() => {
|
233
|
-
// Component mounted
|
234
|
-
console.log('${componentName} mounted');
|
235
|
-
|
236
|
-
return () => {
|
237
|
-
// Component will unmount
|
238
|
-
console.log('${componentName} will unmount');
|
239
|
-
};
|
240
|
-
}, []);
|
241
|
-
|
242
|
-
return (
|
243
|
-
<div className="${componentName.toLowerCase()}">
|
244
|
-
<h2>{title}</h2>
|
245
|
-
<p>Count: {count}</p>
|
246
|
-
<button onClick={() => setCount(count + 1)}>Increment</button>
|
247
|
-
</div>
|
248
|
-
);
|
249
|
-
}
|
250
|
-
`;
|
251
|
-
} else if (type === 'class') {
|
252
|
-
content = `import { Component } from 'frontend-hamroun';
|
253
|
-
|
254
|
-
interface ${componentName}Props {
|
255
|
-
title?: string;
|
256
|
-
}
|
257
|
-
|
258
|
-
interface ${componentName}State {
|
259
|
-
count: number;
|
260
|
-
}
|
261
|
-
|
262
|
-
export class ${componentName} extends Component<${componentName}Props, ${componentName}State> {
|
263
|
-
constructor(props: ${componentName}Props) {
|
264
|
-
super(props);
|
265
|
-
this.state = {
|
266
|
-
count: 0
|
267
|
-
};
|
268
|
-
}
|
269
|
-
|
270
|
-
componentDidMount() {
|
271
|
-
console.log('${componentName} mounted');
|
272
|
-
}
|
273
|
-
|
274
|
-
componentWillUnmount() {
|
275
|
-
console.log('${componentName} will unmount');
|
276
|
-
}
|
277
|
-
|
278
|
-
increment = () => {
|
279
|
-
this.setState({ count: this.state.count + 1 });
|
280
|
-
}
|
281
|
-
|
282
|
-
render() {
|
283
|
-
const { title = 'Default Title' } = this.props;
|
284
|
-
|
285
|
-
return (
|
286
|
-
<div className="${componentName.toLowerCase()}">
|
287
|
-
<h2>{title}</h2>
|
288
|
-
<p>Count: {this.state.count}</p>
|
289
|
-
<button onClick={this.increment}>Increment</button>
|
290
|
-
</div>
|
291
|
-
);
|
292
|
-
}
|
293
|
-
}
|
294
|
-
`;
|
295
|
-
}
|
296
|
-
|
297
|
-
// Write component file
|
298
|
-
await writeFile(componentFilePath, content, 'utf8');
|
299
|
-
|
300
|
-
log.success(`Component ${componentName} created at ${componentFilePath}`);
|
301
|
-
} catch (error) {
|
302
|
-
log.error(`Failed to generate component: ${error.message}`);
|
303
|
-
} finally {
|
304
|
-
rl.close();
|
305
|
-
}
|
306
|
-
}
|
307
|
-
|
308
|
-
/**
|
309
|
-
* Generate a model
|
310
|
-
*/
|
311
|
-
async function generateModel(modelName, options = {}) {
|
312
|
-
try {
|
313
|
-
const { directory = 'models' } = options;
|
314
|
-
const targetDir = path.resolve(process.cwd(), directory);
|
315
|
-
|
316
|
-
// Create directory if it doesn't exist
|
317
|
-
await mkdir(targetDir, { recursive: true });
|
318
|
-
|
319
|
-
// Determine file path and content
|
320
|
-
const modelFileName = `${modelName}.ts`;
|
321
|
-
const modelFilePath = path.join(targetDir, modelFileName);
|
322
|
-
|
323
|
-
// Check if file already exists
|
324
|
-
if (fs.existsSync(modelFilePath)) {
|
325
|
-
const overwrite = await question(`Model ${modelName} already exists. Overwrite? (y/N): `);
|
326
|
-
if (overwrite.toLowerCase() !== 'y') {
|
327
|
-
log.info('Model generation cancelled.');
|
328
|
-
rl.close();
|
329
|
-
return;
|
330
|
-
}
|
331
|
-
}
|
332
|
-
|
333
|
-
// Generate model content
|
334
|
-
const content = `import { Schema } from 'mongoose';
|
335
|
-
import { createModel, FieldTypes } from 'frontend-hamroun';
|
336
|
-
|
337
|
-
// Define the model interface
|
338
|
-
export interface ${modelName} {
|
339
|
-
_id?: string;
|
340
|
-
name: string;
|
341
|
-
description?: string;
|
342
|
-
createdAt: Date;
|
343
|
-
updatedAt: Date;
|
344
|
-
}
|
345
|
-
|
346
|
-
// Define the schema
|
347
|
-
const ${modelName.toLowerCase()}Schema = new Schema<${modelName}>({
|
348
|
-
name: FieldTypes.Required(FieldTypes.String),
|
349
|
-
description: FieldTypes.String,
|
350
|
-
createdAt: FieldTypes.Default(FieldTypes.Date, Date.now),
|
351
|
-
updatedAt: FieldTypes.Default(FieldTypes.Date, Date.now)
|
352
|
-
}, {
|
353
|
-
timestamps: true
|
354
|
-
});
|
355
|
-
|
356
|
-
// Create and export the model
|
357
|
-
export const ${modelName}Model = createModel<${modelName}>('${modelName}', ${modelName.toLowerCase()}Schema);
|
358
|
-
`;
|
359
|
-
|
360
|
-
// Write model file
|
361
|
-
await writeFile(modelFilePath, content, 'utf8');
|
362
|
-
|
363
|
-
log.success(`Model ${modelName} created at ${modelFilePath}`);
|
364
|
-
} catch (error) {
|
365
|
-
log.error(`Failed to generate model: ${error.message}`);
|
366
|
-
} finally {
|
367
|
-
rl.close();
|
368
|
-
}
|
369
|
-
}
|
370
|
-
|
371
|
-
/**
|
372
|
-
* Generate an API route
|
373
|
-
*/
|
374
|
-
async function generateRoute(routeName, options = {}) {
|
375
|
-
try {
|
376
|
-
const { directory = 'routes', model = null } = options;
|
377
|
-
const targetDir = path.resolve(process.cwd(), directory);
|
378
|
-
|
379
|
-
// Create directory if it doesn't exist
|
380
|
-
await mkdir(targetDir, { recursive: true });
|
381
|
-
|
382
|
-
// Determine file path and content
|
383
|
-
const routeFileName = `${routeName}.ts`;
|
384
|
-
const routeFilePath = path.join(targetDir, routeFileName);
|
385
|
-
|
386
|
-
// Check if file already exists
|
387
|
-
if (fs.existsSync(routeFilePath)) {
|
388
|
-
const overwrite = await question(`Route ${routeName} already exists. Overwrite? (y/N): `);
|
389
|
-
if (overwrite.toLowerCase() !== 'y') {
|
390
|
-
log.info('Route generation cancelled.');
|
391
|
-
rl.close();
|
392
|
-
return;
|
393
|
-
}
|
394
|
-
}
|
395
|
-
|
396
|
-
// Generate route content based on whether a model is specified
|
397
|
-
let content = '';
|
398
|
-
if (model) {
|
399
|
-
content = `import { Router } from 'express';
|
400
|
-
import { createRestEndpoints, asyncHandler, sendSuccess, sendError } from 'frontend-hamroun';
|
401
|
-
import { ${model}Model } from '../models/${model}';
|
402
|
-
|
403
|
-
// Create router with standard CRUD endpoints
|
404
|
-
const router = createRestEndpoints(${model}Model);
|
405
|
-
|
406
|
-
// Add custom endpoints
|
407
|
-
router.get('/count', asyncHandler(async (req, res) => {
|
408
|
-
const count = await ${model}Model.count();
|
409
|
-
sendSuccess(res, { count });
|
410
|
-
}));
|
411
|
-
|
412
|
-
export default router;
|
413
|
-
`;
|
414
|
-
} else {
|
415
|
-
content = `import { Router } from 'express';
|
416
|
-
import { asyncHandler, sendSuccess, sendError } from 'frontend-hamroun';
|
417
|
-
|
418
|
-
const router = Router();
|
419
|
-
|
420
|
-
// Define routes
|
421
|
-
router.get('/', asyncHandler(async (req, res) => {
|
422
|
-
sendSuccess(res, { message: 'Welcome to the ${routeName} API' });
|
423
|
-
}));
|
424
|
-
|
425
|
-
router.post('/', asyncHandler(async (req, res) => {
|
426
|
-
const { name } = req.body;
|
427
|
-
|
428
|
-
if (!name) {
|
429
|
-
return sendError(res, 'Name is required', 400);
|
430
|
-
}
|
431
|
-
|
432
|
-
sendSuccess(res, { name }, 'Resource created', 201);
|
433
|
-
}));
|
434
|
-
|
435
|
-
router.get('/:id', asyncHandler(async (req, res) => {
|
436
|
-
const { id } = req.params;
|
437
|
-
|
438
|
-
// Implement your logic here
|
439
|
-
|
440
|
-
sendSuccess(res, { id, name: 'Sample Item' });
|
441
|
-
}));
|
442
|
-
|
443
|
-
export default router;
|
444
|
-
`;
|
445
|
-
}
|
446
|
-
|
447
|
-
// Write route file
|
448
|
-
await writeFile(routeFilePath, content, 'utf8');
|
449
|
-
|
450
|
-
log.success(`Route ${routeName} created at ${routeFilePath}`);
|
451
|
-
} catch (error) {
|
452
|
-
log.error(`Failed to generate route: ${error.message}`);
|
453
|
-
} finally {
|
454
|
-
rl.close();
|
455
|
-
}
|
456
|
-
}
|
457
|
-
|
458
|
-
/**
|
459
|
-
* Print usage instructions
|
460
|
-
*/
|
461
|
-
function printHelp() {
|
462
|
-
log.title('Hamroun CLI Tool');
|
463
|
-
console.log('Usage: hamroun <command> [options]\n');
|
464
|
-
|
465
|
-
console.log('Commands:');
|
466
|
-
console.log(' create <project-name> [template] Create a new Hamroun project');
|
467
|
-
console.log(' generate <type> <name> Generate a component, model, or route');
|
468
|
-
console.log(' help Show this help message\n');
|
469
|
-
|
470
|
-
console.log('Examples:');
|
471
|
-
console.log(' hamroun create my-app full-stack Create a new full-stack project');
|
472
|
-
console.log(' hamroun generate component Button Generate a new component');
|
473
|
-
console.log(' hamroun generate model User Generate a new model');
|
474
|
-
console.log(' hamroun generate route users Generate a new API route\n');
|
475
|
-
|
476
|
-
console.log('Available templates:');
|
477
|
-
console.log(' full-stack Full-stack application with API and frontend');
|
478
|
-
console.log(' frontend Frontend-only application');
|
479
|
-
console.log(' api API-only application');
|
480
|
-
console.log(' minimal Minimal starter with essential files only\n');
|
481
|
-
|
482
|
-
rl.close();
|
483
|
-
}
|
484
|
-
|
485
|
-
/**
|
486
|
-
* Main CLI function
|
487
|
-
*/
|
488
|
-
async function main() {
|
489
|
-
const args = process.argv.slice(2);
|
490
|
-
const command = args[0];
|
491
|
-
|
492
|
-
if (!command || command === 'help') {
|
493
|
-
printHelp();
|
494
|
-
return;
|
495
|
-
}
|
496
|
-
|
497
|
-
switch (command) {
|
498
|
-
case 'create':
|
499
|
-
const projectName = args[1];
|
500
|
-
const templateName = args[2] || 'full-stack';
|
501
|
-
|
502
|
-
if (!projectName) {
|
503
|
-
log.error('Project name is required.');
|
504
|
-
printHelp();
|
505
|
-
return;
|
506
|
-
}
|
507
|
-
|
508
|
-
await createProject(projectName, templateName);
|
509
|
-
break;
|
510
|
-
|
511
|
-
case 'generate':
|
512
|
-
const type = args[1];
|
513
|
-
const name = args[2];
|
514
|
-
|
515
|
-
if (!type || !name) {
|
516
|
-
log.error('Type and name are required.');
|
517
|
-
printHelp();
|
518
|
-
return;
|
519
|
-
}
|
520
|
-
|
521
|
-
switch (type) {
|
522
|
-
case 'component':
|
523
|
-
await generateComponent(name, {
|
524
|
-
type: args[3] || 'functional'
|
525
|
-
});
|
526
|
-
break;
|
527
|
-
|
528
|
-
case 'model':
|
529
|
-
await generateModel(name);
|
530
|
-
break;
|
531
|
-
|
532
|
-
case 'route':
|
533
|
-
await generateRoute(name, {
|
534
|
-
model: args[3]
|
535
|
-
});
|
536
|
-
break;
|
537
|
-
|
538
|
-
default:
|
539
|
-
log.error(`Unknown generator type: ${type}`);
|
540
|
-
printHelp();
|
541
|
-
}
|
542
|
-
break;
|
543
|
-
|
544
|
-
default:
|
545
|
-
log.error(`Unknown command: ${command}`);
|
546
|
-
printHelp();
|
547
|
-
}
|
548
|
-
}
|
549
|
-
|
550
|
-
// Run CLI
|
551
|
-
main().catch(error => {
|
552
|
-
log.error(`An error occurred: ${error.message}`);
|
553
|
-
rl.close();
|
554
|
-
});
|