@morojs/cli 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.
Files changed (47) hide show
  1. package/README.md +300 -0
  2. package/bin/cli.js +4 -0
  3. package/dist/cli.d.ts +4 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +426 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/commands/config.d.ts +17 -0
  8. package/dist/commands/config.d.ts.map +1 -0
  9. package/dist/commands/config.js +334 -0
  10. package/dist/commands/config.js.map +1 -0
  11. package/dist/commands/database.d.ts +38 -0
  12. package/dist/commands/database.d.ts.map +1 -0
  13. package/dist/commands/database.js +523 -0
  14. package/dist/commands/database.js.map +1 -0
  15. package/dist/commands/deploy.d.ts +18 -0
  16. package/dist/commands/deploy.d.ts.map +1 -0
  17. package/dist/commands/deploy.js +166 -0
  18. package/dist/commands/deploy.js.map +1 -0
  19. package/dist/commands/dev.d.ts +27 -0
  20. package/dist/commands/dev.d.ts.map +1 -0
  21. package/dist/commands/dev.js +216 -0
  22. package/dist/commands/dev.js.map +1 -0
  23. package/dist/commands/init.d.ts +27 -0
  24. package/dist/commands/init.d.ts.map +1 -0
  25. package/dist/commands/init.js +1061 -0
  26. package/dist/commands/init.js.map +1 -0
  27. package/dist/commands/middleware.d.ts +11 -0
  28. package/dist/commands/middleware.d.ts.map +1 -0
  29. package/dist/commands/middleware.js +229 -0
  30. package/dist/commands/middleware.js.map +1 -0
  31. package/dist/index.d.ts +4 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +11 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/logger.d.ts +7 -0
  36. package/dist/logger.d.ts.map +1 -0
  37. package/dist/logger.js +23 -0
  38. package/dist/logger.js.map +1 -0
  39. package/dist/module-stub-generator.d.ts +16 -0
  40. package/dist/module-stub-generator.d.ts.map +1 -0
  41. package/dist/module-stub-generator.js +505 -0
  42. package/dist/module-stub-generator.js.map +1 -0
  43. package/dist/utils/terminal.d.ts +11 -0
  44. package/dist/utils/terminal.d.ts.map +1 -0
  45. package/dist/utils/terminal.js +43 -0
  46. package/dist/utils/terminal.js.map +1 -0
  47. package/package.json +97 -0
@@ -0,0 +1,1061 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ProjectInitializer = void 0;
7
+ // Project Initialization - Create new MoroJS projects with intelligent setup
8
+ const promises_1 = require("fs/promises");
9
+ const path_1 = require("path");
10
+ const fs_1 = require("fs");
11
+ const logger_1 = require("../logger");
12
+ const inquirer_1 = __importDefault(require("inquirer"));
13
+ const chalk_1 = __importDefault(require("chalk"));
14
+ const ora_1 = __importDefault(require("ora"));
15
+ const boxen_1 = __importDefault(require("boxen"));
16
+ const figlet_1 = __importDefault(require("figlet"));
17
+ const terminal_1 = require("../utils/terminal");
18
+ class ProjectInitializer {
19
+ constructor() {
20
+ this.logger = (0, logger_1.createFrameworkLogger)('ProjectInitializer');
21
+ }
22
+ async initializeProject(projectName, options) {
23
+ // Welcome banner
24
+ console.log(chalk_1.default.cyan(figlet_1.default.textSync('MoroJS', { horizontalLayout: 'full' })));
25
+ console.log((0, boxen_1.default)(chalk_1.default.green('Creating your magical MoroJS project...'), {
26
+ padding: 1,
27
+ margin: 1,
28
+ borderStyle: 'round',
29
+ borderColor: 'green',
30
+ }));
31
+ const projectPath = (0, path_1.resolve)(process.cwd(), projectName);
32
+ // Check if directory already exists
33
+ if ((0, fs_1.existsSync)(projectPath)) {
34
+ const { overwrite } = await inquirer_1.default.prompt([
35
+ {
36
+ type: 'confirm',
37
+ name: 'overwrite',
38
+ message: `Directory "${projectName}" already exists. Overwrite?`,
39
+ default: false,
40
+ },
41
+ ]);
42
+ if (!overwrite) {
43
+ this.logger.info('Project initialization cancelled.', 'Init');
44
+ return;
45
+ }
46
+ }
47
+ // Interactive prompts if options not provided
48
+ const config = await this.gatherProjectConfig(options);
49
+ const spinner = (0, ora_1.default)('Creating project structure...').start();
50
+ try {
51
+ // Create project directory
52
+ await (0, promises_1.mkdir)(projectPath, { recursive: true });
53
+ // Generate project files
54
+ await Promise.all([
55
+ this.generatePackageJson(projectPath, projectName, config),
56
+ this.generateTsConfig(projectPath),
57
+ this.generateMainApp(projectPath, config),
58
+ this.generateEnvFiles(projectPath, config),
59
+ this.generateMoroConfig(projectPath, config),
60
+ this.generateGitignore(projectPath),
61
+ this.generateReadme(projectPath, projectName, config),
62
+ this.generateDockerfile(projectPath, config),
63
+ this.generateProjectStructure(projectPath, config),
64
+ ]);
65
+ spinner.succeed('Project structure created!');
66
+ // Initialize git repository
67
+ if (!config.skipGit) {
68
+ const gitSpinner = (0, ora_1.default)('Initializing Git repository...').start();
69
+ try {
70
+ await (0, terminal_1.runTerminalCmd)('git init', { cwd: projectPath });
71
+ await (0, terminal_1.runTerminalCmd)('git add .', { cwd: projectPath });
72
+ await (0, terminal_1.runTerminalCmd)('git commit -m "Initial commit: MoroJS project setup"', {
73
+ cwd: projectPath,
74
+ });
75
+ gitSpinner.succeed('Git repository initialized!');
76
+ }
77
+ catch (error) {
78
+ gitSpinner.warn('Git initialization skipped (git not available)');
79
+ }
80
+ }
81
+ // Install dependencies
82
+ if (!config.skipInstall) {
83
+ const installSpinner = (0, ora_1.default)('Installing dependencies...').start();
84
+ try {
85
+ await (0, terminal_1.runTerminalCmd)('npm install', { cwd: projectPath });
86
+ installSpinner.succeed('Dependencies installed!');
87
+ }
88
+ catch (error) {
89
+ installSpinner.fail('Failed to install dependencies. Run "npm install" manually.');
90
+ }
91
+ }
92
+ // Success message with next steps
93
+ this.displaySuccessMessage(projectName, config);
94
+ }
95
+ catch (error) {
96
+ spinner.fail('Project creation failed!');
97
+ throw error;
98
+ }
99
+ }
100
+ async gatherProjectConfig(options) {
101
+ const questions = [];
102
+ if (!options.runtime) {
103
+ questions.push({
104
+ type: 'list',
105
+ name: 'runtime',
106
+ message: 'Select runtime adapter:',
107
+ choices: [
108
+ { name: ' Node.js (Traditional server)', value: 'node' },
109
+ { name: 'Vercel Edge (Edge runtime)', value: 'vercel-edge' },
110
+ { name: 'AWS Lambda (Serverless)', value: 'aws-lambda' },
111
+ { name: ' Cloudflare Workers (Edge workers)', value: 'cloudflare-workers' },
112
+ ],
113
+ default: 'node',
114
+ });
115
+ }
116
+ if (!options.database) {
117
+ questions.push({
118
+ type: 'list',
119
+ name: 'database',
120
+ message: ' Select primary database:',
121
+ choices: [
122
+ { name: 'PostgreSQL (Recommended)', value: 'postgresql' },
123
+ { name: 'MySQL/MariaDB', value: 'mysql' },
124
+ { name: 'SQLite (Lightweight)', value: 'sqlite' },
125
+ { name: 'MongoDB (Document DB)', value: 'mongodb' },
126
+ { name: 'Redis (Cache/Sessions)', value: 'redis' },
127
+ { name: 'Drizzle ORM (Type-safe)', value: 'drizzle' },
128
+ { name: '❌ Skip database setup', value: 'none' },
129
+ ],
130
+ });
131
+ }
132
+ if (!options.template) {
133
+ questions.push({
134
+ type: 'list',
135
+ name: 'template',
136
+ message: 'Select project template:',
137
+ choices: [
138
+ { name: 'API Server (REST/GraphQL)', value: 'api' },
139
+ { name: 'Full-stack (API + Frontend)', value: 'fullstack' },
140
+ { name: 'Microservice (Minimal)', value: 'microservice' },
141
+ ],
142
+ default: 'api',
143
+ });
144
+ }
145
+ if (!options.features) {
146
+ questions.push({
147
+ type: 'checkbox',
148
+ name: 'features',
149
+ message: 'Select features to include:',
150
+ choices: [
151
+ { name: 'Authentication & Authorization', value: 'auth', checked: true },
152
+ { name: ' CORS & Security Headers', value: 'cors', checked: true },
153
+ { name: ' Compression & Performance', value: 'compression', checked: true },
154
+ { name: 'WebSocket Support', value: 'websocket' },
155
+ { name: 'API Documentation (OpenAPI)', value: 'docs', checked: true },
156
+ { name: 'Rate Limiting', value: 'rate-limit', checked: true },
157
+ { name: 'Caching Layer', value: 'cache' },
158
+ { name: 'Circuit Breaker', value: 'circuit-breaker' },
159
+ { name: 'Monitoring & Metrics', value: 'monitoring' },
160
+ { name: 'Testing Setup', value: 'testing', checked: true },
161
+ ],
162
+ });
163
+ }
164
+ const answers = await inquirer_1.default.prompt(questions);
165
+ return {
166
+ runtime: options.runtime || answers.runtime,
167
+ database: options.database || answers.database,
168
+ template: options.template || answers.template,
169
+ features: options.features ? options.features.split(',') : answers.features || [],
170
+ skipGit: options.skipGit || false,
171
+ skipInstall: options.skipInstall || false,
172
+ };
173
+ }
174
+ async generatePackageJson(projectPath, projectName, config) {
175
+ const packageJson = {
176
+ name: projectName,
177
+ version: '1.0.0',
178
+ description: `MoroJS ${config.template} project`,
179
+ main: 'dist/index.js',
180
+ type: 'module',
181
+ scripts: {
182
+ dev: 'morojs-cli dev',
183
+ build: 'morojs-cli build',
184
+ start: 'node dist/index.js',
185
+ test: 'morojs-cli test',
186
+ lint: 'morojs-cli lint',
187
+ 'db:migrate': 'morojs-cli db migrate --up',
188
+ 'db:seed': 'morojs-cli db seed',
189
+ ...(config.runtime === 'vercel-edge' && { 'deploy:vercel': 'morojs-cli deploy vercel' }),
190
+ ...(config.runtime === 'aws-lambda' && { 'deploy:lambda': 'morojs-cli deploy lambda' }),
191
+ ...(config.runtime === 'cloudflare-workers' && {
192
+ 'deploy:workers': 'morojs-cli deploy workers',
193
+ }),
194
+ },
195
+ dependencies: {
196
+ '@morojs/moro': '^1.0.0',
197
+ ...(config.database === 'postgresql' && { pg: '^8.11.3', '@types/pg': '^8.10.9' }),
198
+ ...(config.database === 'mysql' && { mysql2: '^3.6.5' }),
199
+ ...(config.database === 'mongodb' && { mongodb: '^6.3.0' }),
200
+ ...(config.database === 'redis' && { redis: '^4.6.10' }),
201
+ ...(config.database === 'drizzle' && {
202
+ 'drizzle-orm': '^0.29.1',
203
+ 'drizzle-kit': '^0.20.6',
204
+ }),
205
+ ...(config.features.includes('auth') && { jsonwebtoken: '^9.0.2', bcryptjs: '^2.4.3' }),
206
+ zod: '^3.22.4',
207
+ },
208
+ devDependencies: {
209
+ '@morojs/cli': '^1.0.0',
210
+ '@types/node': '^20.10.0',
211
+ typescript: '^5.3.2',
212
+ ...(config.features.includes('testing') && {
213
+ jest: '^29.7.0',
214
+ '@types/jest': '^29.5.8',
215
+ 'ts-jest': '^29.1.1',
216
+ supertest: '^6.3.3',
217
+ '@types/supertest': '^2.0.16',
218
+ }),
219
+ },
220
+ engines: {
221
+ node: '>=18.0.0',
222
+ },
223
+ };
224
+ await (0, promises_1.writeFile)((0, path_1.join)(projectPath, 'package.json'), JSON.stringify(packageJson, null, 2));
225
+ }
226
+ async generateTsConfig(projectPath) {
227
+ const tsConfig = {
228
+ compilerOptions: {
229
+ target: 'ES2022',
230
+ module: 'ESNext',
231
+ moduleResolution: 'node',
232
+ strict: true,
233
+ esModuleInterop: true,
234
+ skipLibCheck: true,
235
+ forceConsistentCasingInFileNames: true,
236
+ outDir: './dist',
237
+ rootDir: './src',
238
+ declaration: true,
239
+ declarationMap: true,
240
+ sourceMap: true,
241
+ removeComments: false,
242
+ experimentalDecorators: true,
243
+ emitDecoratorMetadata: true,
244
+ resolveJsonModule: true,
245
+ allowSyntheticDefaultImports: true,
246
+ },
247
+ include: ['src/**/*'],
248
+ exclude: ['node_modules', 'dist', '**/*.test.ts', '**/*.spec.ts'],
249
+ };
250
+ await (0, promises_1.writeFile)((0, path_1.join)(projectPath, 'tsconfig.json'), JSON.stringify(tsConfig, null, 2));
251
+ }
252
+ async generateMainApp(projectPath, config) {
253
+ await (0, promises_1.mkdir)((0, path_1.join)(projectPath, 'src'), { recursive: true });
254
+ const runtimeImports = {
255
+ node: 'createAppNode',
256
+ 'vercel-edge': 'createAppEdge',
257
+ 'aws-lambda': 'createAppLambda',
258
+ 'cloudflare-workers': 'createAppWorker',
259
+ };
260
+ const appContent = `// ${config.template.toUpperCase()} MoroJS Application
261
+ import { ${runtimeImports[config.runtime]}, logger } from '@morojs/moro';
262
+ ${config.database !== 'none' ? `import { setupDatabase } from './database';` : ''}
263
+ ${config.features.includes('auth') ? `import { setupAuth } from './middleware/auth';` : ''}
264
+
265
+ // Create MoroJS application with ${config.runtime} runtime
266
+ const app = ${runtimeImports[config.runtime]}({
267
+ runtime: { type: '${config.runtime}' },
268
+ ${config.features.includes('cors') ? `cors: true,` : ''}
269
+ ${config.features.includes('compression') ? `compression: true,` : ''}
270
+ logger: {
271
+ level: process.env.LOG_LEVEL || 'info',
272
+ format: 'pretty'
273
+ }
274
+ });
275
+
276
+ // Database setup
277
+ ${config.database !== 'none' ? `await setupDatabase(app);` : ''}
278
+
279
+ // Middleware setup
280
+ ${config.features.includes('auth') ? `await setupAuth(app);` : ''}
281
+
282
+ // API Documentation
283
+ ${config.features.includes('docs')
284
+ ? `
285
+ app.enableDocs({
286
+ title: '${config.template.charAt(0).toUpperCase() + config.template.slice(1)} API',
287
+ description: 'MoroJS ${config.template} application',
288
+ version: '1.0.0',
289
+ basePath: '/docs'
290
+ });`
291
+ : ''}
292
+
293
+ // Health check endpoint
294
+ app.get('/health', async (req, res) => {
295
+ return {
296
+ success: true,
297
+ status: 'healthy',
298
+ timestamp: new Date().toISOString(),
299
+ runtime: '${config.runtime}',
300
+ version: '1.0.0'
301
+ };
302
+ });
303
+
304
+ // Welcome endpoint
305
+ app.get('/', async (req, res) => {
306
+ return {
307
+ message: 'Welcome to your MoroJS ${config.template}!',
308
+ docs: '/docs',
309
+ health: '/health'
310
+ };
311
+ });
312
+
313
+ // Auto-discover and load modules
314
+ // Modules will be automatically loaded from ./modules directory
315
+
316
+ ${config.runtime === 'node'
317
+ ? `
318
+ // Start server (Node.js only)
319
+ const PORT = process.env.PORT || 3000;
320
+ const HOST = process.env.HOST || 'localhost';
321
+
322
+ app.listen(PORT, HOST, () => {
323
+ logger.info(\`\${config.template.charAt(0).toUpperCase() + config.template.slice(1)} server running!\`);
324
+ logger.info(\`HTTP: http://\${HOST}:\${PORT}\`);
325
+ ${config.features.includes('websocket') ? `logger.info(\`🔌 WebSocket: ws://\${HOST}:\${PORT}\`);` : ''}
326
+ ${config.features.includes('docs') ? `logger.info(\`Docs: http://\${HOST}:\${PORT}/docs\`);` : ''}
327
+ });
328
+ `
329
+ : `
330
+ // Export handler for ${config.runtime}
331
+ export default app.getHandler();
332
+ `}
333
+
334
+ export { app };`;
335
+ await (0, promises_1.writeFile)((0, path_1.join)(projectPath, 'src', 'index.ts'), appContent);
336
+ }
337
+ async generateEnvFiles(projectPath, config) {
338
+ const envContent = `# MoroJS ${config.template.toUpperCase()} Configuration
339
+
340
+ # Server Configuration
341
+ NODE_ENV=development
342
+ PORT=3000
343
+ HOST=localhost
344
+ LOG_LEVEL=info
345
+
346
+ # Database Configuration
347
+ ${config.database === 'postgresql'
348
+ ? `
349
+ DATABASE_URL=postgresql://username:password@localhost:5432/database_name
350
+ POSTGRES_HOST=localhost
351
+ POSTGRES_PORT=5432
352
+ POSTGRES_USER=username
353
+ POSTGRES_PASSWORD=password
354
+ POSTGRES_DB=database_name`
355
+ : ''}
356
+ ${config.database === 'mysql'
357
+ ? `
358
+ DATABASE_URL=mysql://username:password@localhost:3306/database_name
359
+ MYSQL_HOST=localhost
360
+ MYSQL_PORT=3306
361
+ MYSQL_USER=username
362
+ MYSQL_PASSWORD=password
363
+ MYSQL_DATABASE=database_name`
364
+ : ''}
365
+ ${config.database === 'mongodb'
366
+ ? `
367
+ DATABASE_URL=mongodb://localhost:27017/database_name
368
+ MONGODB_URI=mongodb://localhost:27017/database_name`
369
+ : ''}
370
+ ${config.database === 'redis'
371
+ ? `
372
+ REDIS_URL=redis://localhost:6379
373
+ REDIS_HOST=localhost
374
+ REDIS_PORT=6379`
375
+ : ''}
376
+
377
+ # Security Configuration
378
+ ${config.features.includes('auth')
379
+ ? `
380
+ JWT_SECRET=your-super-secret-jwt-key-change-this-in-production
381
+ JWT_EXPIRES_IN=24h
382
+ BCRYPT_ROUNDS=12`
383
+ : ''}
384
+
385
+ # External Services (Optional)
386
+ ${config.features.includes('monitoring')
387
+ ? `
388
+ # Monitoring
389
+ SENTRY_DSN=
390
+ NEW_RELIC_LICENSE_KEY=`
391
+ : ''}
392
+
393
+ # Runtime-specific Configuration
394
+ ${config.runtime === 'aws-lambda'
395
+ ? `
396
+ AWS_REGION=us-east-1
397
+ AWS_LAMBDA_FUNCTION_NAME=${config.name}`
398
+ : ''}
399
+ ${config.runtime === 'cloudflare-workers'
400
+ ? `
401
+ CLOUDFLARE_ACCOUNT_ID=
402
+ CLOUDFLARE_API_TOKEN=`
403
+ : ''}
404
+ `;
405
+ await (0, promises_1.writeFile)((0, path_1.join)(projectPath, '.env'), envContent);
406
+ await (0, promises_1.writeFile)((0, path_1.join)(projectPath, '.env.example'), envContent.replace(/=.*/g, '='));
407
+ }
408
+ async generateMoroConfig(projectPath, config) {
409
+ const configContent = `// MoroJS Configuration
410
+ import { z } from 'zod';
411
+
412
+ export default {
413
+ server: {
414
+ port: parseInt(process.env.PORT || '3000'),
415
+ host: process.env.HOST || 'localhost',
416
+ environment: process.env.NODE_ENV || 'development'
417
+ },
418
+
419
+ ${config.database !== 'none'
420
+ ? `database: {
421
+ ${config.database === 'postgresql' ? `url: process.env.DATABASE_URL` : ''}
422
+ ${config.database === 'mysql' ? `url: process.env.DATABASE_URL` : ''}
423
+ ${config.database === 'mongodb' ? `url: process.env.MONGODB_URI` : ''}
424
+ ${config.database === 'redis' ? `redis: { url: process.env.REDIS_URL }` : ''}
425
+ },`
426
+ : ''}
427
+
428
+ logging: {
429
+ level: process.env.LOG_LEVEL || 'info',
430
+ format: 'pretty',
431
+ enableColors: true,
432
+ enableTimestamp: true
433
+ },
434
+
435
+ security: {
436
+ cors: {
437
+ enabled: true,
438
+ origin: process.env.NODE_ENV === 'production' ? false : '*'
439
+ },
440
+ helmet: {
441
+ enabled: true
442
+ }
443
+ },
444
+
445
+ performance: {
446
+ compression: {
447
+ enabled: true,
448
+ level: 6
449
+ },
450
+ circuitBreaker: {
451
+ enabled: ${config.features.includes('circuit-breaker')}
452
+ }
453
+ },
454
+
455
+ modules: {
456
+ cache: {
457
+ enabled: ${config.features.includes('cache')},
458
+ defaultTtl: 300
459
+ },
460
+ rateLimit: {
461
+ enabled: ${config.features.includes('rate-limit')},
462
+ defaultRequests: 100,
463
+ defaultWindow: 60000
464
+ }
465
+ }
466
+ };`;
467
+ await (0, promises_1.writeFile)((0, path_1.join)(projectPath, 'moro.config.js'), configContent);
468
+ }
469
+ async generateGitignore(projectPath) {
470
+ const gitignoreContent = `# Dependencies
471
+ node_modules/
472
+ npm-debug.log*
473
+ yarn-debug.log*
474
+ yarn-error.log*
475
+
476
+ # Build outputs
477
+ dist/
478
+ build/
479
+ .next/
480
+ out/
481
+
482
+ # Environment files
483
+ .env
484
+ .env.local
485
+ .env.production
486
+ .env.staging
487
+
488
+ # Logs
489
+ logs/
490
+ *.log
491
+
492
+ # Runtime data
493
+ pids/
494
+ *.pid
495
+ *.seed
496
+ *.pid.lock
497
+
498
+ # Coverage directory used by tools like istanbul
499
+ coverage/
500
+ *.lcov
501
+
502
+ # nyc test coverage
503
+ .nyc_output
504
+
505
+ # Dependency directories
506
+ jspm_packages/
507
+
508
+ # Optional npm cache directory
509
+ .npm
510
+
511
+ # Optional eslint cache
512
+ .eslintcache
513
+
514
+ # Microbundle cache
515
+ .rpt2_cache/
516
+ .rts2_cache_cjs/
517
+ .rts2_cache_es/
518
+ .rts2_cache_umd/
519
+
520
+ # Optional REPL history
521
+ .node_repl_history
522
+
523
+ # Output of 'npm pack'
524
+ *.tgz
525
+
526
+ # Yarn Integrity file
527
+ .yarn-integrity
528
+
529
+ # parcel-bundler cache (https://parceljs.org/)
530
+ .cache
531
+ .parcel-cache
532
+
533
+ # Next.js build output
534
+ .next
535
+
536
+ # Nuxt.js build / generate output
537
+ .nuxt
538
+
539
+ # Storybook build outputs
540
+ .out
541
+ .storybook-out
542
+
543
+ # Temporary folders
544
+ tmp/
545
+ temp/
546
+
547
+ # Editor directories and files
548
+ .vscode/
549
+ .idea/
550
+ *.suo
551
+ *.ntvs*
552
+ *.njsproj
553
+ *.sln
554
+ *.sw?
555
+
556
+ # OS generated files
557
+ .DS_Store
558
+ .DS_Store?
559
+ ._*
560
+ .Spotlight-V100
561
+ .Trashes
562
+ ehthumbs.db
563
+ Thumbs.db
564
+
565
+ # Database
566
+ *.sqlite
567
+ *.db
568
+
569
+ # SSL certificates
570
+ *.pem
571
+ *.key
572
+ *.crt`;
573
+ await (0, promises_1.writeFile)((0, path_1.join)(projectPath, '.gitignore'), gitignoreContent);
574
+ }
575
+ async generateReadme(projectPath, projectName, config) {
576
+ const readmeContent = `# ${projectName}
577
+
578
+ **MoroJS ${config.template.charAt(0).toUpperCase() + config.template.slice(1)} Project**
579
+
580
+ A modern, high-performance ${config.template} built with the MoroJS framework, featuring ${config.runtime} runtime and ${config.database !== 'none' ? config.database : 'no'} database integration.
581
+
582
+ ## Features
583
+
584
+ ${config.features
585
+ .map((feature) => {
586
+ const featureMap = {
587
+ auth: 'Authentication & Authorization',
588
+ cors: 'CORS & Security Headers',
589
+ compression: 'Compression & Performance',
590
+ websocket: 'WebSocket Support',
591
+ docs: 'API Documentation (OpenAPI)',
592
+ 'rate-limit': 'Rate Limiting',
593
+ cache: 'Caching Layer',
594
+ 'circuit-breaker': 'Circuit Breaker',
595
+ monitoring: 'Monitoring & Metrics',
596
+ testing: 'Testing Setup',
597
+ };
598
+ return `- ${featureMap[feature] || feature}`;
599
+ })
600
+ .join('\n')}
601
+
602
+ ## Quick Start
603
+
604
+ \`\`\`bash
605
+ # Install dependencies
606
+ npm install
607
+
608
+ # Start development server
609
+ npm run dev
610
+
611
+ # Build for production
612
+ npm run build
613
+
614
+ # Start production server
615
+ npm start
616
+ \`\`\`
617
+
618
+ ## Endpoints
619
+
620
+ - **Health Check**: \`GET /health\`
621
+ - **Welcome**: \`GET /\`
622
+ ${config.features.includes('docs') ? `- **API Docs**: \`GET /docs\`` : ''}
623
+
624
+ ## Database
625
+
626
+ ${config.database !== 'none'
627
+ ? `
628
+ This project uses **${config.database}** as the primary database.
629
+
630
+ \`\`\`bash
631
+ # Run migrations
632
+ npm run db:migrate
633
+
634
+ # Seed database
635
+ npm run db:seed
636
+ \`\`\`
637
+ `
638
+ : 'No database configured. Add one with `morojs-cli db setup <type>`.'}
639
+
640
+ ## Development
641
+
642
+ \`\`\`bash
643
+ # Development with hot reload
644
+ npm run dev
645
+
646
+ # Run tests
647
+ npm test
648
+
649
+ # Lint code
650
+ npm run lint
651
+ \`\`\`
652
+
653
+ ## Module Development
654
+
655
+ Create new modules with the MoroJS CLI:
656
+
657
+ \`\`\`bash
658
+ # Create a new module
659
+ morojs-cli module create users --features=database,auth,cache
660
+
661
+ # List all modules
662
+ morojs-cli module list
663
+ \`\`\`
664
+
665
+ ## Deployment
666
+
667
+ ${config.runtime === 'vercel-edge'
668
+ ? `
669
+ ### Vercel Edge Runtime
670
+
671
+ \`\`\`bash
672
+ npm run deploy:vercel
673
+ \`\`\`
674
+ `
675
+ : ''}
676
+ ${config.runtime === 'aws-lambda'
677
+ ? `
678
+ ### AWS Lambda
679
+
680
+ \`\`\`bash
681
+ npm run deploy:lambda
682
+ \`\`\`
683
+ `
684
+ : ''}
685
+ ${config.runtime === 'cloudflare-workers'
686
+ ? `
687
+ ### Cloudflare Workers
688
+
689
+ \`\`\`bash
690
+ npm run deploy:workers
691
+ \`\`\`
692
+ `
693
+ : ''}
694
+ ${config.runtime === 'node'
695
+ ? `
696
+ ### Traditional Node.js Deployment
697
+
698
+ Deploy to any Node.js hosting provider:
699
+
700
+ \`\`\`bash
701
+ npm run build
702
+ npm start
703
+ \`\`\`
704
+ `
705
+ : ''}
706
+
707
+ ## Documentation
708
+
709
+ - [MoroJS Framework](https://morojs.com)
710
+ - [API Documentation](${config.features.includes('docs') ? '/docs' : 'https://morojs.com/docs'})
711
+ - [Module Development Guide](https://morojs.com/modules)
712
+
713
+ ## 🤝 Contributing
714
+
715
+ 1. Fork the repository
716
+ 2. Create your feature branch (\`git checkout -b feature/amazing-feature\`)
717
+ 3. Commit your changes (\`git commit -m 'Add amazing feature'\`)
718
+ 4. Push to the branch (\`git push origin feature/amazing-feature\`)
719
+ 5. Open a Pull Request
720
+
721
+ ## 📄 License
722
+
723
+ This project is licensed under the MIT License.
724
+
725
+ ---
726
+
727
+ **Built with ❤️ using [MoroJS Framework](https://morojs.com)**`;
728
+ await (0, promises_1.writeFile)((0, path_1.join)(projectPath, 'README.md'), readmeContent);
729
+ }
730
+ async generateDockerfile(projectPath, config) {
731
+ if (config.runtime !== 'node')
732
+ return; // Docker only for Node.js runtime
733
+ const dockerContent = `# MoroJS ${config.template.charAt(0).toUpperCase() + config.template.slice(1)} - Docker Configuration
734
+
735
+ # Use official Node.js runtime as base image
736
+ FROM node:18-alpine AS base
737
+
738
+ # Install dependencies only when needed
739
+ FROM base AS deps
740
+ # Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
741
+ RUN apk add --no-cache libc6-compat
742
+ WORKDIR /app
743
+
744
+ # Copy package files
745
+ COPY package*.json ./
746
+ RUN npm ci --only=production
747
+
748
+ # Rebuild the source code only when needed
749
+ FROM base AS builder
750
+ WORKDIR /app
751
+ COPY --from=deps /app/node_modules ./node_modules
752
+ COPY . .
753
+
754
+ # Build the application
755
+ RUN npm run build
756
+
757
+ # Production image, copy all the files and run the app
758
+ FROM base AS runner
759
+ WORKDIR /app
760
+
761
+ ENV NODE_ENV production
762
+
763
+ RUN addgroup --system --gid 1001 morojs
764
+ RUN adduser --system --uid 1001 morojs
765
+
766
+ # Copy built application
767
+ COPY --from=builder --chown=morojs:morojs /app/dist ./dist
768
+ COPY --from=builder --chown=morojs:morojs /app/node_modules ./node_modules
769
+ COPY --from=builder --chown=morojs:morojs /app/package.json ./package.json
770
+
771
+ USER morojs
772
+
773
+ EXPOSE 3000
774
+
775
+ ENV PORT 3000
776
+ ENV HOST 0.0.0.0
777
+
778
+ CMD ["node", "dist/index.js"]`;
779
+ await (0, promises_1.writeFile)((0, path_1.join)(projectPath, 'Dockerfile'), dockerContent);
780
+ // Docker compose for development with database
781
+ if (config.database !== 'none') {
782
+ const composeContent = `version: '3.8'
783
+
784
+ services:
785
+ app:
786
+ build: .
787
+ ports:
788
+ - "3000:3000"
789
+ environment:
790
+ - NODE_ENV=development
791
+ - DATABASE_URL=\${DATABASE_URL}
792
+ depends_on:
793
+ - database
794
+ volumes:
795
+ - .:/app
796
+ - /app/node_modules
797
+
798
+ ${config.database === 'postgresql'
799
+ ? `
800
+ database:
801
+ image: postgres:15-alpine
802
+ environment:
803
+ POSTGRES_DB: \${POSTGRES_DB:-myapp}
804
+ POSTGRES_USER: \${POSTGRES_USER:-postgres}
805
+ POSTGRES_PASSWORD: \${POSTGRES_PASSWORD:-password}
806
+ ports:
807
+ - "5432:5432"
808
+ volumes:
809
+ - postgres_data:/var/lib/postgresql/data
810
+
811
+ volumes:
812
+ postgres_data:`
813
+ : ''}
814
+ ${config.database === 'mysql'
815
+ ? `
816
+ database:
817
+ image: mysql:8.0
818
+ environment:
819
+ MYSQL_ROOT_PASSWORD: \${MYSQL_PASSWORD:-password}
820
+ MYSQL_DATABASE: \${MYSQL_DATABASE:-myapp}
821
+ MYSQL_USER: \${MYSQL_USER:-mysql}
822
+ MYSQL_PASSWORD: \${MYSQL_PASSWORD:-password}
823
+ ports:
824
+ - "3306:3306"
825
+ volumes:
826
+ - mysql_data:/var/lib/mysql
827
+
828
+ volumes:
829
+ mysql_data:`
830
+ : ''}
831
+ ${config.database === 'mongodb'
832
+ ? `
833
+ database:
834
+ image: mongo:7
835
+ environment:
836
+ MONGO_INITDB_DATABASE: \${MONGO_DATABASE:-myapp}
837
+ ports:
838
+ - "27017:27017"
839
+ volumes:
840
+ - mongo_data:/data/db
841
+
842
+ volumes:
843
+ mongo_data:`
844
+ : ''}
845
+ ${config.database === 'redis'
846
+ ? `
847
+ redis:
848
+ image: redis:7-alpine
849
+ ports:
850
+ - "6379:6379"
851
+ volumes:
852
+ - redis_data:/data
853
+
854
+ volumes:
855
+ redis_data:`
856
+ : ''}`;
857
+ await (0, promises_1.writeFile)((0, path_1.join)(projectPath, 'docker-compose.yml'), composeContent);
858
+ }
859
+ }
860
+ async generateProjectStructure(projectPath, config) {
861
+ // Create directory structure
862
+ const directories = ['src/modules', 'src/middleware', 'src/types', 'src/utils'];
863
+ if (config.database !== 'none') {
864
+ directories.push('src/database', 'src/database/migrations', 'src/database/seeds');
865
+ }
866
+ if (config.features.includes('testing')) {
867
+ directories.push('tests', 'tests/unit', 'tests/integration');
868
+ }
869
+ for (const dir of directories) {
870
+ await (0, promises_1.mkdir)((0, path_1.join)(projectPath, dir), { recursive: true });
871
+ }
872
+ // Generate database setup if database is configured
873
+ if (config.database !== 'none') {
874
+ await this.generateDatabaseSetup(projectPath, config);
875
+ }
876
+ // Generate auth middleware if auth is enabled
877
+ if (config.features.includes('auth')) {
878
+ await this.generateAuthMiddleware(projectPath);
879
+ }
880
+ // Generate test setup if testing is enabled
881
+ if (config.features.includes('testing')) {
882
+ await this.generateTestSetup(projectPath);
883
+ }
884
+ }
885
+ async generateDatabaseSetup(projectPath, config) {
886
+ const dbSetupContent = `// Database Setup and Configuration
887
+ import { ${config.database.charAt(0).toUpperCase() + config.database.slice(1)}Adapter } from '@morojs/moro';
888
+ import { createFrameworkLogger } from '@morojs/moro';
889
+
890
+ const logger = createFrameworkLogger('Database');
891
+
892
+ export async function setupDatabase(app: any): Promise<void> {
893
+ try {
894
+ const adapter = new ${config.database.charAt(0).toUpperCase() + config.database.slice(1)}Adapter({
895
+ ${config.database === 'postgresql' || config.database === 'mysql'
896
+ ? `
897
+ url: process.env.DATABASE_URL,
898
+ host: process.env.${config.database.toUpperCase()}_HOST,
899
+ port: parseInt(process.env.${config.database.toUpperCase()}_PORT || '${config.database === 'postgresql' ? '5432' : '3306'}'),
900
+ username: process.env.${config.database.toUpperCase()}_USER,
901
+ password: process.env.${config.database.toUpperCase()}_PASSWORD,
902
+ database: process.env.${config.database.toUpperCase()}_${config.database === 'postgresql' ? 'DB' : 'DATABASE'}`
903
+ : ''}
904
+ ${config.database === 'mongodb'
905
+ ? `
906
+ url: process.env.MONGODB_URI`
907
+ : ''}
908
+ ${config.database === 'redis'
909
+ ? `
910
+ url: process.env.REDIS_URL,
911
+ host: process.env.REDIS_HOST,
912
+ port: parseInt(process.env.REDIS_PORT || '6379')`
913
+ : ''}
914
+ });
915
+
916
+ await adapter.connect();
917
+ app.database(adapter);
918
+
919
+ logger.info('✅ Database connected successfully', 'Database');
920
+ } catch (error) {
921
+ logger.error('❌ Database connection failed:', error, 'Database');
922
+ throw error;
923
+ }
924
+ }`;
925
+ await (0, promises_1.writeFile)((0, path_1.join)(projectPath, 'src', 'database', 'index.ts'), dbSetupContent);
926
+ }
927
+ async generateAuthMiddleware(projectPath) {
928
+ const authContent = `// Authentication Middleware
929
+ import { auth } from '@morojs/moro';
930
+ import jwt from 'jsonwebtoken';
931
+ import bcrypt from 'bcryptjs';
932
+
933
+ const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key';
934
+
935
+ export async function setupAuth(app: any): Promise<void> {
936
+ // JWT Authentication middleware
937
+ app.use(auth({
938
+ secret: JWT_SECRET,
939
+ algorithms: ['HS256'],
940
+ optional: true // Make auth optional by default
941
+ }));
942
+
943
+ // Login endpoint
944
+ app.post('/auth/login', async (req: any, res: any) => {
945
+ const { email, password } = req.body;
946
+
947
+ // TODO: Implement user lookup from database
948
+ // const user = await getUserByEmail(email);
949
+ // const isValid = await bcrypt.compare(password, user.password);
950
+
951
+ // Mock implementation
952
+ if (email === 'admin@example.com' && password === 'password') {
953
+ const token = jwt.sign(
954
+ { userId: 1, email, roles: ['admin'] },
955
+ JWT_SECRET,
956
+ { expiresIn: process.env.JWT_EXPIRES_IN || '24h' }
957
+ );
958
+
959
+ return { success: true, token, user: { id: 1, email, roles: ['admin'] } };
960
+ }
961
+
962
+ res.status(401);
963
+ return { success: false, error: 'Invalid credentials' };
964
+ });
965
+
966
+ // Protected route example
967
+ app.get('/auth/profile', async (req: any, res: any) => {
968
+ if (!req.user) {
969
+ res.status(401);
970
+ return { success: false, error: 'Authentication required' };
971
+ }
972
+
973
+ return { success: true, user: req.user };
974
+ });
975
+ }`;
976
+ await (0, promises_1.writeFile)((0, path_1.join)(projectPath, 'src', 'middleware', 'auth.ts'), authContent);
977
+ }
978
+ async generateTestSetup(projectPath) {
979
+ // Jest configuration
980
+ const jestConfig = {
981
+ preset: 'ts-jest',
982
+ testEnvironment: 'node',
983
+ roots: ['<rootDir>/src', '<rootDir>/tests'],
984
+ testMatch: ['**/__tests__/**/*.ts', '**/?(*.)+(spec|test).ts'],
985
+ collectCoverageFrom: [
986
+ 'src/**/*.ts',
987
+ '!src/**/*.d.ts',
988
+ '!src/**/*.test.ts',
989
+ '!src/**/*.spec.ts',
990
+ ],
991
+ coverageDirectory: 'coverage',
992
+ coverageReporters: ['text', 'lcov', 'html'],
993
+ };
994
+ await (0, promises_1.writeFile)((0, path_1.join)(projectPath, 'jest.config.js'), `module.exports = ${JSON.stringify(jestConfig, null, 2)};`);
995
+ // Sample test file
996
+ const testContent = `// Sample Integration Test
997
+ import request from 'supertest';
998
+ import { app } from '../src/index';
999
+
1000
+ describe('API Health Checks', () => {
1001
+ test('GET /health should return healthy status', async () => {
1002
+ const response = await request(app.core.getHttpServer().getServer())
1003
+ .get('/health')
1004
+ .expect(200);
1005
+
1006
+ expect(response.body).toMatchObject({
1007
+ success: true,
1008
+ status: 'healthy'
1009
+ });
1010
+ });
1011
+
1012
+ test('GET / should return welcome message', async () => {
1013
+ const response = await request(app.core.getHttpServer().getServer())
1014
+ .get('/')
1015
+ .expect(200);
1016
+
1017
+ expect(response.body).toHaveProperty('message');
1018
+ expect(response.body.message).toContain('Welcome');
1019
+ });
1020
+ });`;
1021
+ await (0, promises_1.writeFile)((0, path_1.join)(projectPath, 'tests', 'app.test.ts'), testContent);
1022
+ }
1023
+ displaySuccessMessage(projectName, config) {
1024
+ console.log((0, boxen_1.default)(chalk_1.default.green(`
1025
+ Project "${projectName}" created successfully!
1026
+
1027
+ Project Structure:
1028
+ ${projectName}/
1029
+ ├── src/
1030
+ │ ├── index.ts # Main application
1031
+ │ ├── modules/ # MoroJS modules
1032
+ │ ├── middleware/ # Custom middleware
1033
+ ${config.database !== 'none' ? '│ ├── database/ # Database setup' : ''}
1034
+ │ └── types/ # TypeScript types
1035
+ ├── package.json
1036
+ ├── tsconfig.json
1037
+ ├── moro.config.js
1038
+ └── README.md
1039
+
1040
+ Next Steps:
1041
+ cd ${projectName}
1042
+ ${config.skipInstall ? 'npm install' : ''}
1043
+ npm run dev
1044
+
1045
+ Resources:
1046
+ • Documentation: https://morojs.com
1047
+ • Examples: morojs-cli examples
1048
+ • Create modules: morojs-cli module create <name>
1049
+
1050
+ Features Enabled:
1051
+ ${config.features.map((f) => ` • ${f}`).join('\n')}
1052
+ `), {
1053
+ padding: 1,
1054
+ margin: 1,
1055
+ borderStyle: 'round',
1056
+ borderColor: 'green',
1057
+ }));
1058
+ }
1059
+ }
1060
+ exports.ProjectInitializer = ProjectInitializer;
1061
+ //# sourceMappingURL=init.js.map