easybuild-nox 1.0.0

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.
@@ -0,0 +1,556 @@
1
+ const { Command } = require('commander');
2
+ const inquirer = require('inquirer');
3
+ const path = require('path');
4
+ const fs = require('fs-extra');
5
+ const logger = require('../utils/logger');
6
+ const ProjectDetector = require('../utils/detector');
7
+
8
+ const generateCommand = new Command('generate')
9
+ .description('Generate code scaffolding')
10
+ .alias('g')
11
+ .argument('[type]', 'Type to generate (component, api, page, model, service, util)')
12
+ .argument('[name]', 'Name of the file to generate')
13
+ .option('-d, --dir <directory>', 'Directory to generate in')
14
+ .action(async (type, name, options) => {
15
+ logger.header('🔧 Code Generator');
16
+
17
+ const detector = new ProjectDetector();
18
+
19
+ try {
20
+ const projectInfo = await detector.detect();
21
+
22
+ if (!type) {
23
+ const typeAnswer = await inquirer.prompt([
24
+ {
25
+ type: 'list',
26
+ name: 'type',
27
+ message: 'What do you want to generate?',
28
+ choices: [
29
+ { name: 'Component (React/Vue/Svelte)', value: 'component' },
30
+ { name: 'API Route', value: 'api' },
31
+ { name: 'Page', value: 'page' },
32
+ { name: 'Model (Database)', value: 'model' },
33
+ { name: 'Service', value: 'service' },
34
+ { name: 'Utility Function', value: 'util' },
35
+ { name: 'Hook (React)', value: 'hook' },
36
+ { name: 'Context (React)', value: 'context' },
37
+ { name: 'Middleware', value: 'middleware' },
38
+ { name: 'Controller', value: 'controller' },
39
+ { name: 'Test File', value: 'test' },
40
+ { name: 'Config File', value: 'config' },
41
+ ],
42
+ },
43
+ ]);
44
+ type = typeAnswer.type;
45
+ }
46
+
47
+ if (!name) {
48
+ const nameAnswer = await inquirer.prompt([
49
+ {
50
+ type: 'input',
51
+ name: 'name',
52
+ message: `Name for the ${type}:`,
53
+ validate: (input) => input.length > 0 || 'Name is required',
54
+ },
55
+ ]);
56
+ name = nameAnswer.name;
57
+ }
58
+
59
+ await generateCode(type, name, projectInfo, options);
60
+ } catch (error) {
61
+ logger.error(`Generation failed: ${error.message}`);
62
+ process.exit(1);
63
+ }
64
+ });
65
+
66
+ async function generateCode(type, name, projectInfo, options) {
67
+ const generators = {
68
+ component: generateComponent,
69
+ api: generateApi,
70
+ page: generatePage,
71
+ model: generateModel,
72
+ service: generateService,
73
+ util: generateUtil,
74
+ hook: generateHook,
75
+ context: generateContext,
76
+ middleware: generateMiddleware,
77
+ controller: generateController,
78
+ test: generateTest,
79
+ config: generateConfig,
80
+ };
81
+
82
+ const generator = generators[type];
83
+ if (!generator) {
84
+ logger.error(`Unknown type: ${type}`);
85
+ return;
86
+ }
87
+
88
+ await generator(name, projectInfo, options);
89
+ }
90
+
91
+ async function generateComponent(name, projectInfo, options) {
92
+ const framework = projectInfo.framework?.name || 'react';
93
+ const dir = options.dir || getComponentDir(projectInfo);
94
+
95
+ await fs.ensureDir(dir);
96
+
97
+ let content;
98
+ let fileName;
99
+
100
+ if (framework === 'vue') {
101
+ fileName = `${name}.vue`;
102
+ content = `<template>
103
+ <div class="${name.toLowerCase()}">
104
+ <h1>${name}</h1>
105
+ </div>
106
+ </template>
107
+
108
+ <script setup>
109
+ // ${name} component logic
110
+ </script>
111
+
112
+ <style scoped>
113
+ .${name.toLowerCase()} {
114
+ /* Styles */
115
+ }
116
+ </style>`;
117
+ } else if (framework === 'svelte' || framework === 'sveltekit') {
118
+ fileName = `${name}.svelte`;
119
+ content = `<script>
120
+ // ${name} component logic
121
+ </script>
122
+
123
+ <div class="${name.toLowerCase()}">
124
+ <h1>${name}</h1>
125
+ </div>
126
+
127
+ <style>
128
+ .${name.toLowerCase()} {
129
+ /* Styles */
130
+ }
131
+ </style>`;
132
+ } else {
133
+ // React/JSX
134
+ fileName = `${name}.jsx`;
135
+ content = `import React from 'react';
136
+
137
+ const ${name} = () => {
138
+ return (
139
+ <div className="${name.toLowerCase()}">
140
+ <h1>${name}</h1>
141
+ </div>
142
+ );
143
+ };
144
+
145
+ export default ${name};`;
146
+ }
147
+
148
+ const filePath = path.join(dir, fileName);
149
+ await fs.writeFile(filePath, content);
150
+ logger.success(`Generated ${fileName} in ${dir}`);
151
+ }
152
+
153
+ async function generateApi(name, projectInfo, options) {
154
+ const framework = projectInfo.framework?.name || 'express';
155
+ const dir = options.dir || 'src/routes';
156
+
157
+ await fs.ensureDir(dir);
158
+
159
+ let content;
160
+ let fileName;
161
+
162
+ if (framework === 'nextjs') {
163
+ fileName = `${name}/route.js`;
164
+ content = `import { NextResponse } from 'next/server';
165
+
166
+ export async function GET(request) {
167
+ return NextResponse.json({ message: 'Hello from ${name}' });
168
+ }
169
+
170
+ export async function POST(request) {
171
+ const body = await request.json();
172
+ return NextResponse.json({ data: body }, { status: 201 });
173
+ }`;
174
+ } else if (framework === 'nuxt') {
175
+ fileName = `${name}.js`;
176
+ content = `export default defineEventHandler((event) => {
177
+ return { message: 'Hello from ${name}' };
178
+ });`;
179
+ } else {
180
+ fileName = `${name}.js`;
181
+ content = `const express = require('express');
182
+ const router = express.Router();
183
+
184
+ router.get('/', (req, res) => {
185
+ res.json({ message: 'Hello from ${name}' });
186
+ });
187
+
188
+ router.post('/', (req, res) => {
189
+ res.status(201).json({ data: req.body });
190
+ });
191
+
192
+ module.exports = router;`;
193
+ }
194
+
195
+ const filePath = path.join(dir, fileName);
196
+ await fs.ensureDir(path.dirname(filePath));
197
+ await fs.writeFile(filePath, content);
198
+ logger.success(`Generated ${fileName} in ${dir}`);
199
+ }
200
+
201
+ async function generatePage(name, projectInfo, options) {
202
+ const framework = projectInfo.framework?.name || 'react';
203
+ const dir = options.dir || getPageDir(projectInfo);
204
+
205
+ await fs.ensureDir(dir);
206
+
207
+ let content;
208
+ let fileName;
209
+
210
+ if (framework === 'nextjs') {
211
+ fileName = `${name}/page.js`;
212
+ content = `export default function ${name}Page() {
213
+ return (
214
+ <div>
215
+ <h1>${name}</h1>
216
+ <p>Welcome to the ${name} page</p>
217
+ </div>
218
+ );
219
+ }`;
220
+ } else if (framework === 'nuxt') {
221
+ fileName = `${name}.vue`;
222
+ content = `<template>
223
+ <div>
224
+ <h1>${name}</h1>
225
+ <p>Welcome to the ${name} page</p>
226
+ </div>
227
+ </template>
228
+
229
+ <script setup>
230
+ </script>`;
231
+ } else if (framework === 'astro') {
232
+ fileName = `${name}.astro`;
233
+ content = `---
234
+ // ${name} page
235
+ ---
236
+
237
+ <html lang="en">
238
+ <head>
239
+ <meta charset="UTF-8" />
240
+ <title>${name}</title>
241
+ </head>
242
+ <body>
243
+ <h1>${name}</h1>
244
+ <p>Welcome to the ${name} page</p>
245
+ </body>
246
+ </html>`;
247
+ } else {
248
+ fileName = `${name}.jsx`;
249
+ content = `const ${name} = () => {
250
+ return (
251
+ <div>
252
+ <h1>${name}</h1>
253
+ <p>Welcome to the ${name} page</p>
254
+ </div>
255
+ );
256
+ };
257
+
258
+ export default ${name};`;
259
+ }
260
+
261
+ const filePath = path.join(dir, fileName);
262
+ await fs.ensureDir(path.dirname(filePath));
263
+ await fs.writeFile(filePath, content);
264
+ logger.success(`Generated ${fileName}`);
265
+ }
266
+
267
+ async function generateModel(name, projectInfo, options) {
268
+ const dir = options.dir || 'src/models';
269
+ await fs.ensureDir(dir);
270
+
271
+ const fileName = `${name.toLowerCase()}.js`;
272
+ const content = `const mongoose = require('mongoose');
273
+
274
+ const ${name}Schema = new mongoose.Schema({
275
+ // Add your fields here
276
+ name: {
277
+ type: String,
278
+ required: true,
279
+ },
280
+ createdAt: {
281
+ type: Date,
282
+ default: Date.now,
283
+ },
284
+ updatedAt: {
285
+ type: Date,
286
+ default: Date.now,
287
+ },
288
+ });
289
+
290
+ ${name}Schema.pre('save', function(next) {
291
+ this.updatedAt = Date.now();
292
+ next();
293
+ });
294
+
295
+ module.exports = mongoose.model('${name}', ${name}Schema);`;
296
+
297
+ const filePath = path.join(dir, fileName);
298
+ await fs.writeFile(filePath, content);
299
+ logger.success(`Generated ${fileName} in ${dir}`);
300
+ }
301
+
302
+ async function generateService(name, projectInfo, options) {
303
+ const dir = options.dir || 'src/services';
304
+ await fs.ensureDir(dir);
305
+
306
+ const fileName = `${name.toLowerCase()}Service.js`;
307
+ const content = `class ${name}Service {
308
+ constructor() {
309
+ // Initialize service
310
+ }
311
+
312
+ async findAll() {
313
+ // Implement find all
314
+ return [];
315
+ }
316
+
317
+ async findById(id) {
318
+ // Implement find by id
319
+ return null;
320
+ }
321
+
322
+ async create(data) {
323
+ // Implement create
324
+ return data;
325
+ }
326
+
327
+ async update(id, data) {
328
+ // Implement update
329
+ return data;
330
+ }
331
+
332
+ async delete(id) {
333
+ // Implement delete
334
+ return true;
335
+ }
336
+ }
337
+
338
+ module.exports = new ${name}Service();`;
339
+
340
+ const filePath = path.join(dir, fileName);
341
+ await fs.writeFile(filePath, content);
342
+ logger.success(`Generated ${fileName} in ${dir}`);
343
+ }
344
+
345
+ async function generateUtil(name, projectInfo, options) {
346
+ const dir = options.dir || 'src/utils';
347
+ await fs.ensureDir(dir);
348
+
349
+ const fileName = `${name.toLowerCase()}.js`;
350
+ const content = `/**
351
+ * ${name} utility function
352
+ */
353
+
354
+ const ${name} = () => {
355
+ // Implement utility
356
+ };
357
+
358
+ module.exports = ${name};`;
359
+
360
+ const filePath = path.join(dir, fileName);
361
+ await fs.writeFile(filePath, content);
362
+ logger.success(`Generated ${fileName} in ${dir}`);
363
+ }
364
+
365
+ async function generateHook(name, projectInfo, options) {
366
+ const dir = options.dir || 'src/hooks';
367
+ await fs.ensureDir(dir);
368
+
369
+ const fileName = `use${name}.js`;
370
+ const content = `import { useState, useEffect } from 'react';
371
+
372
+ const use${name} = () => {
373
+ const [state, setState] = useState(null);
374
+
375
+ useEffect(() => {
376
+ // Hook logic
377
+ }, []);
378
+
379
+ return { state };
380
+ };
381
+
382
+ export default use${name};`;
383
+
384
+ const filePath = path.join(dir, fileName);
385
+ await fs.writeFile(filePath, content);
386
+ logger.success(`Generated ${fileName} in ${dir}`);
387
+ }
388
+
389
+ async function generateContext(name, projectInfo, options) {
390
+ const dir = options.dir || 'src/context';
391
+ await fs.ensureDir(dir);
392
+
393
+ const fileName = `${name}Context.js`;
394
+ const content = `import { createContext, useContext, useState } from 'react';
395
+
396
+ const ${name}Context = createContext();
397
+
398
+ export const ${name}Provider = ({ children }) => {
399
+ const [state, setState] = useState(null);
400
+
401
+ return (
402
+ <${name}Context.Provider value={{ state, setState }}>
403
+ {children}
404
+ </${name}Context.Provider>
405
+ );
406
+ };
407
+
408
+ export const use${name} = () => {
409
+ const context = useContext(${name}Context);
410
+ if (!context) {
411
+ throw new Error('use${name} must be used within a ${name}Provider');
412
+ }
413
+ return context;
414
+ };`;
415
+
416
+ const filePath = path.join(dir, fileName);
417
+ await fs.writeFile(filePath, content);
418
+ logger.success(`Generated ${fileName} in ${dir}`);
419
+ }
420
+
421
+ async function generateMiddleware(name, projectInfo, options) {
422
+ const dir = options.dir || 'src/middleware';
423
+ await fs.ensureDir(dir);
424
+
425
+ const fileName = `${name.toLowerCase()}.js`;
426
+ const content = `const ${name} = (req, res, next) => {
427
+ // Middleware logic
428
+ console.log('${name} middleware executed');
429
+ next();
430
+ };
431
+
432
+ module.exports = ${name};`;
433
+
434
+ const filePath = path.join(dir, fileName);
435
+ await fs.writeFile(filePath, content);
436
+ logger.success(`Generated ${fileName} in ${dir}`);
437
+ }
438
+
439
+ async function generateController(name, projectInfo, options) {
440
+ const dir = options.dir || 'src/controllers';
441
+ await fs.ensureDir(dir);
442
+
443
+ const fileName = `${name.toLowerCase()}Controller.js`;
444
+ const content = `class ${name}Controller {
445
+ async getAll(req, res) {
446
+ try {
447
+ // Implement get all
448
+ res.json({ data: [] });
449
+ } catch (error) {
450
+ res.status(500).json({ error: error.message });
451
+ }
452
+ }
453
+
454
+ async getById(req, res) {
455
+ try {
456
+ const { id } = req.params;
457
+ // Implement get by id
458
+ res.json({ data: null });
459
+ } catch (error) {
460
+ res.status(500).json({ error: error.message });
461
+ }
462
+ }
463
+
464
+ async create(req, res) {
465
+ try {
466
+ const data = req.body;
467
+ // Implement create
468
+ res.status(201).json({ data });
469
+ } catch (error) {
470
+ res.status(500).json({ error: error.message });
471
+ }
472
+ }
473
+
474
+ async update(req, res) {
475
+ try {
476
+ const { id } = req.params;
477
+ const data = req.body;
478
+ // Implement update
479
+ res.json({ data });
480
+ } catch (error) {
481
+ res.status(500).json({ error: error.message });
482
+ }
483
+ }
484
+
485
+ async delete(req, res) {
486
+ try {
487
+ const { id } = req.params;
488
+ // Implement delete
489
+ res.json({ message: 'Deleted successfully' });
490
+ } catch (error) {
491
+ res.status(500).json({ error: error.message });
492
+ }
493
+ }
494
+ }
495
+
496
+ module.exports = new ${name}Controller();`;
497
+
498
+ const filePath = path.join(dir, fileName);
499
+ await fs.writeFile(filePath, content);
500
+ logger.success(`Generated ${fileName} in ${dir}`);
501
+ }
502
+
503
+ async function generateTest(name, projectInfo, options) {
504
+ const dir = options.dir || '__tests__';
505
+ await fs.ensureDir(dir);
506
+
507
+ const fileName = `${name.toLowerCase()}.test.js`;
508
+ const content = `const ${name} = require('../src/${name}');
509
+
510
+ describe('${name}', () => {
511
+ test('should work correctly', () => {
512
+ expect(true).toBe(true);
513
+ });
514
+
515
+ test('should handle edge cases', () => {
516
+ // Add test cases
517
+ });
518
+ });`;
519
+
520
+ const filePath = path.join(dir, fileName);
521
+ await fs.writeFile(filePath, content);
522
+ logger.success(`Generated ${fileName} in ${dir}`);
523
+ }
524
+
525
+ async function generateConfig(name, projectInfo, options) {
526
+ const dir = options.dir || '.';
527
+ const fileName = `${name.toLowerCase()}.config.js`;
528
+ const content = `module.exports = {
529
+ // ${name} configuration
530
+ };`;
531
+
532
+ const filePath = path.join(dir, fileName);
533
+ await fs.writeFile(filePath, content);
534
+ logger.success(`Generated ${fileName} in ${dir}`);
535
+ }
536
+
537
+ function getComponentDir(projectInfo) {
538
+ const framework = projectInfo.framework?.name;
539
+ if (framework === 'nextjs') return 'src/components';
540
+ if (framework === 'nuxt') return 'components';
541
+ if (framework === 'vue') return 'src/components';
542
+ if (framework === 'svelte' || framework === 'sveltekit') return 'src/lib/components';
543
+ if (framework === 'astro') return 'src/components';
544
+ return 'src/components';
545
+ }
546
+
547
+ function getPageDir(projectInfo) {
548
+ const framework = projectInfo.framework?.name;
549
+ if (framework === 'nextjs') return 'app';
550
+ if (framework === 'nuxt') return 'pages';
551
+ if (framework === 'sveltekit') return 'src/routes';
552
+ if (framework === 'astro') return 'src/pages';
553
+ return 'src/pages';
554
+ }
555
+
556
+ module.exports = generateCommand;