@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.
- package/README.md +300 -0
- package/bin/cli.js +4 -0
- package/dist/cli.d.ts +4 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +426 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/config.d.ts +17 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +334 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/database.d.ts +38 -0
- package/dist/commands/database.d.ts.map +1 -0
- package/dist/commands/database.js +523 -0
- package/dist/commands/database.js.map +1 -0
- package/dist/commands/deploy.d.ts +18 -0
- package/dist/commands/deploy.d.ts.map +1 -0
- package/dist/commands/deploy.js +166 -0
- package/dist/commands/deploy.js.map +1 -0
- package/dist/commands/dev.d.ts +27 -0
- package/dist/commands/dev.d.ts.map +1 -0
- package/dist/commands/dev.js +216 -0
- package/dist/commands/dev.js.map +1 -0
- package/dist/commands/init.d.ts +27 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +1061 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/middleware.d.ts +11 -0
- package/dist/commands/middleware.d.ts.map +1 -0
- package/dist/commands/middleware.js +229 -0
- package/dist/commands/middleware.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +7 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +23 -0
- package/dist/logger.js.map +1 -0
- package/dist/module-stub-generator.d.ts +16 -0
- package/dist/module-stub-generator.d.ts.map +1 -0
- package/dist/module-stub-generator.js +505 -0
- package/dist/module-stub-generator.js.map +1 -0
- package/dist/utils/terminal.d.ts +11 -0
- package/dist/utils/terminal.d.ts.map +1 -0
- package/dist/utils/terminal.js +43 -0
- package/dist/utils/terminal.js.map +1 -0
- 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
|