@diagramers/cli 1.0.23 → 1.0.24
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/dist/services/template-updater.d.ts +2 -0
- package/dist/services/template-updater.d.ts.map +1 -1
- package/dist/services/template-updater.js +66 -14
- package/dist/services/template-updater.js.map +1 -1
- package/package.json +5 -1
- package/templates/api/certs/auth-app-cert.json +13 -0
- package/templates/api/main.ts +10 -0
- package/templates/api/package.json +70 -0
- package/templates/api/src/assets/css/email-template.css +8 -0
- package/templates/api/src/assets/images/logo_large.png +0 -0
- package/templates/api/src/assets/keys/certificate.pem +22 -0
- package/templates/api/src/assets/keys/private-key.pem +28 -0
- package/templates/api/src/config/config-interface.ts +191 -0
- package/templates/api/src/config/development.ts +145 -0
- package/templates/api/src/config/index.ts +59 -0
- package/templates/api/src/config/production.ts +145 -0
- package/templates/api/src/config/staging.ts +144 -0
- package/templates/api/src/config/uat.ts +144 -0
- package/templates/api/src/controllers/account-controller.ts +162 -0
- package/templates/api/src/entities/audit.ts +12 -0
- package/templates/api/src/entities/base-entity.ts +10 -0
- package/templates/api/src/entities/user.ts +71 -0
- package/templates/api/src/helpers/FrameworkHelper.ts +157 -0
- package/templates/api/src/helpers/auth.ts +971 -0
- package/templates/api/src/helpers/cronHelper.ts +170 -0
- package/templates/api/src/helpers/dbcontext.ts +83 -0
- package/templates/api/src/helpers/encryptionHelper.ts +76 -0
- package/templates/api/src/helpers/enums.ts +258 -0
- package/templates/api/src/helpers/handle-response.ts +49 -0
- package/templates/api/src/helpers/httpHelper.ts +75 -0
- package/templates/api/src/helpers/mailer.ts +152 -0
- package/templates/api/src/helpers/result.ts +47 -0
- package/templates/api/src/helpers/string-helper.ts +27 -0
- package/templates/api/src/routes/account-routes.ts +37 -0
- package/templates/api/src/routes/auth-routes.ts +286 -0
- package/templates/api/src/routes/index.ts +92 -0
- package/templates/api/src/schemas/audit.ts +36 -0
- package/templates/api/src/schemas/otp.ts +52 -0
- package/templates/api/src/schemas/session.ts +57 -0
- package/templates/api/src/schemas/user.ts +125 -0
- package/templates/api/src/server/index.ts +86 -0
- package/templates/api/src/server/socket-server-provider.ts +209 -0
- package/templates/api/src/services/account-service.ts +243 -0
- package/templates/api/src/services/audit-service.ts +56 -0
- package/templates/api/tsconfig.json +16 -0
- package/templates/api/webpack.config.js +66 -0
- package/scripts/publish.sh +0 -58
- package/scripts/setup.sh +0 -38
- package/scripts/version.sh +0 -80
- package/src/commands/api.ts +0 -76
- package/src/commands/extend.ts +0 -35
- package/src/commands/init.ts +0 -32
- package/src/commands/update.ts +0 -25
- package/src/config/template-config.ts +0 -111
- package/src/index.ts +0 -41
- package/src/services/api-generator.ts +0 -378
- package/src/services/project-extender.ts +0 -330
- package/src/services/project-initializer.ts +0 -335
- package/src/services/project-updater.ts +0 -117
- package/src/services/relation-generator.ts +0 -203
- package/src/services/table-generator.ts +0 -114
- package/src/services/template-processor.ts +0 -166
- package/src/services/template-updater.ts +0 -184
- package/tsconfig.json +0 -19
@@ -1,330 +0,0 @@
|
|
1
|
-
import * as fs from 'fs-extra';
|
2
|
-
import * as path from 'path';
|
3
|
-
import chalk from 'chalk';
|
4
|
-
|
5
|
-
export interface Feature {
|
6
|
-
name: string;
|
7
|
-
description: string;
|
8
|
-
files: string[];
|
9
|
-
dependencies?: string[];
|
10
|
-
}
|
11
|
-
|
12
|
-
export class ProjectExtender {
|
13
|
-
private features: Feature[] = [
|
14
|
-
{
|
15
|
-
name: 'auth',
|
16
|
-
description: 'Authentication system with JWT',
|
17
|
-
files: ['src/features/auth/**/*'],
|
18
|
-
dependencies: ['jsonwebtoken', 'bcryptjs']
|
19
|
-
},
|
20
|
-
{
|
21
|
-
name: 'email',
|
22
|
-
description: 'Email notification system',
|
23
|
-
files: ['src/features/email/**/*'],
|
24
|
-
dependencies: ['nodemailer', 'handlebars']
|
25
|
-
},
|
26
|
-
{
|
27
|
-
name: 'socket',
|
28
|
-
description: 'Real-time WebSocket communication',
|
29
|
-
files: ['src/features/socket/**/*'],
|
30
|
-
dependencies: ['socket.io']
|
31
|
-
},
|
32
|
-
{
|
33
|
-
name: 'cron',
|
34
|
-
description: 'Scheduled task system',
|
35
|
-
files: ['src/features/cron/**/*'],
|
36
|
-
dependencies: ['node-cron']
|
37
|
-
},
|
38
|
-
{
|
39
|
-
name: 'audit',
|
40
|
-
description: 'Audit logging system',
|
41
|
-
files: ['src/features/audit/**/*']
|
42
|
-
}
|
43
|
-
];
|
44
|
-
|
45
|
-
async listFeatures(): Promise<void> {
|
46
|
-
console.log(chalk.blue('📋 Available features:'));
|
47
|
-
console.log('');
|
48
|
-
|
49
|
-
this.features.forEach(feature => {
|
50
|
-
console.log(chalk.green(` ${feature.name}`));
|
51
|
-
console.log(chalk.gray(` ${feature.description}`));
|
52
|
-
if (feature.dependencies && feature.dependencies.length > 0) {
|
53
|
-
console.log(chalk.yellow(` Dependencies: ${feature.dependencies.join(', ')}`));
|
54
|
-
}
|
55
|
-
console.log('');
|
56
|
-
});
|
57
|
-
}
|
58
|
-
|
59
|
-
async addFeature(featureName: string): Promise<void> {
|
60
|
-
const feature = this.features.find(f => f.name === featureName);
|
61
|
-
|
62
|
-
if (!feature) {
|
63
|
-
throw new Error(`Feature '${featureName}' not found. Use --list to see available features.`);
|
64
|
-
}
|
65
|
-
|
66
|
-
const projectPath = process.cwd();
|
67
|
-
|
68
|
-
// Check if feature already exists
|
69
|
-
const featurePath = path.join(projectPath, 'src', 'features', featureName);
|
70
|
-
if (await fs.pathExists(featurePath)) {
|
71
|
-
throw new Error(`Feature '${featureName}' already exists in this project.`);
|
72
|
-
}
|
73
|
-
|
74
|
-
// Create feature directory structure
|
75
|
-
await this.createFeatureStructure(projectPath, feature);
|
76
|
-
|
77
|
-
// Add dependencies if any
|
78
|
-
if (feature.dependencies && feature.dependencies.length > 0) {
|
79
|
-
await this.addDependencies(feature.dependencies);
|
80
|
-
}
|
81
|
-
|
82
|
-
// Update main configuration to use the extended feature
|
83
|
-
await this.updateMainConfiguration(projectPath, feature);
|
84
|
-
|
85
|
-
console.log(chalk.green(`✅ Feature '${featureName}' added successfully!`));
|
86
|
-
console.log(chalk.blue(`📝 Main configuration updated to include ${featureName} feature`));
|
87
|
-
}
|
88
|
-
|
89
|
-
private async createFeatureStructure(projectPath: string, feature: Feature): Promise<void> {
|
90
|
-
const featurePath = path.join(projectPath, 'src', 'features', feature.name);
|
91
|
-
|
92
|
-
// Create basic feature structure
|
93
|
-
const structure = {
|
94
|
-
'controllers': 'Feature controllers',
|
95
|
-
'services': 'Feature business logic',
|
96
|
-
'schemas': 'Feature validation schemas',
|
97
|
-
'routes': 'Feature API routes',
|
98
|
-
'types': 'Feature TypeScript types'
|
99
|
-
};
|
100
|
-
|
101
|
-
for (const [dir, description] of Object.entries(structure)) {
|
102
|
-
const dirPath = path.join(featurePath, dir);
|
103
|
-
await fs.ensureDir(dirPath);
|
104
|
-
|
105
|
-
// Create index file
|
106
|
-
const indexContent = `// ${description} for ${feature.name} feature
|
107
|
-
export * from './${feature.name}-${dir.slice(0, -1)}';
|
108
|
-
`;
|
109
|
-
await fs.writeFile(path.join(dirPath, 'index.ts'), indexContent);
|
110
|
-
}
|
111
|
-
|
112
|
-
// Create main feature file
|
113
|
-
const mainFeatureContent = `import { Router } from 'express';
|
114
|
-
import { ${feature.name}Controller } from './controllers';
|
115
|
-
import { ${feature.name}Service } from './services';
|
116
|
-
|
117
|
-
export class ${this.capitalizeFirst(feature.name)}Feature {
|
118
|
-
private router: Router;
|
119
|
-
private controller: ${feature.name}Controller;
|
120
|
-
private service: ${feature.name}Service;
|
121
|
-
|
122
|
-
constructor() {
|
123
|
-
this.router = Router();
|
124
|
-
this.service = new ${feature.name}Service();
|
125
|
-
this.controller = new ${feature.name}Controller(this.service);
|
126
|
-
this.setupRoutes();
|
127
|
-
}
|
128
|
-
|
129
|
-
private setupRoutes(): void {
|
130
|
-
// Define your routes here
|
131
|
-
// this.router.get('/', this.controller.index.bind(this.controller));
|
132
|
-
}
|
133
|
-
|
134
|
-
public getRouter(): Router {
|
135
|
-
return this.router;
|
136
|
-
}
|
137
|
-
}
|
138
|
-
`;
|
139
|
-
|
140
|
-
await fs.writeFile(path.join(featurePath, `${feature.name}.ts`), mainFeatureContent);
|
141
|
-
|
142
|
-
// Create controller
|
143
|
-
const controllerContent = `import { Request, Response } from 'express';
|
144
|
-
import { ${feature.name}Service } from '../services';
|
145
|
-
|
146
|
-
export class ${feature.name}Controller {
|
147
|
-
constructor(private service: ${feature.name}Service) {}
|
148
|
-
|
149
|
-
// Add your controller methods here
|
150
|
-
// async index(req: Request, res: Response): Promise<void> {
|
151
|
-
// try {
|
152
|
-
// const result = await this.service.someMethod();
|
153
|
-
// res.json(result);
|
154
|
-
// } catch (error) {
|
155
|
-
// res.status(500).json({ error: error.message });
|
156
|
-
// }
|
157
|
-
// }
|
158
|
-
}
|
159
|
-
`;
|
160
|
-
|
161
|
-
await fs.writeFile(path.join(featurePath, 'controllers', `${feature.name}-controller.ts`), controllerContent);
|
162
|
-
|
163
|
-
// Create service
|
164
|
-
const serviceContent = `export class ${feature.name}Service {
|
165
|
-
// Add your service methods here
|
166
|
-
// async someMethod(): Promise<any> {
|
167
|
-
// // Implementation
|
168
|
-
// }
|
169
|
-
}
|
170
|
-
`;
|
171
|
-
|
172
|
-
await fs.writeFile(path.join(featurePath, 'services', `${feature.name}-service.ts`), serviceContent);
|
173
|
-
|
174
|
-
// Create types
|
175
|
-
const typesContent = `// TypeScript types for ${feature.name} feature
|
176
|
-
export interface ${this.capitalizeFirst(feature.name)}Config {
|
177
|
-
// Add configuration interface
|
178
|
-
}
|
179
|
-
|
180
|
-
export interface ${this.capitalizeFirst(feature.name)}Data {
|
181
|
-
// Add data interface
|
182
|
-
}
|
183
|
-
`;
|
184
|
-
|
185
|
-
await fs.writeFile(path.join(featurePath, 'types', `${feature.name}-types.ts`), typesContent);
|
186
|
-
}
|
187
|
-
|
188
|
-
private async addDependencies(dependencies: string[]): Promise<void> {
|
189
|
-
console.log(chalk.blue('📦 Installing dependencies...'));
|
190
|
-
|
191
|
-
// This would typically run npm install with the dependencies
|
192
|
-
// For now, we'll just log what would be installed
|
193
|
-
console.log(chalk.yellow(`Would install: ${dependencies.join(', ')}`));
|
194
|
-
console.log(chalk.gray('Run: npm install ' + dependencies.join(' ') + ' to install dependencies'));
|
195
|
-
}
|
196
|
-
|
197
|
-
private async updateMainConfiguration(projectPath: string, feature: Feature): Promise<void> {
|
198
|
-
console.log(chalk.blue(`📝 Updating main configuration for ${feature.name} feature...`));
|
199
|
-
|
200
|
-
// Update main server file to include the feature
|
201
|
-
await this.updateServerFile(projectPath, feature);
|
202
|
-
|
203
|
-
// Update routes index to include feature routes
|
204
|
-
await this.updateRoutesIndex(projectPath, feature);
|
205
|
-
|
206
|
-
// Update package.json scripts if needed
|
207
|
-
await this.updatePackageJson(projectPath, feature);
|
208
|
-
|
209
|
-
console.log(chalk.green(`✅ Main configuration updated for ${feature.name} feature`));
|
210
|
-
}
|
211
|
-
|
212
|
-
private async updateServerFile(projectPath: string, feature: Feature): Promise<void> {
|
213
|
-
const serverFiles = [
|
214
|
-
'src/server/index.ts',
|
215
|
-
'main.ts',
|
216
|
-
'src/index.ts',
|
217
|
-
'lib/main.js'
|
218
|
-
];
|
219
|
-
|
220
|
-
for (const serverFile of serverFiles) {
|
221
|
-
const filePath = path.join(projectPath, serverFile);
|
222
|
-
if (await fs.pathExists(filePath)) {
|
223
|
-
let content = await fs.readFile(filePath, 'utf8');
|
224
|
-
|
225
|
-
// Add import for the feature
|
226
|
-
const importStatement = `import { ${this.capitalizeFirst(feature.name)}Feature } from './features/${feature.name}/${feature.name}';`;
|
227
|
-
|
228
|
-
if (!content.includes(importStatement)) {
|
229
|
-
// Find the last import statement and add after it
|
230
|
-
const importRegex = /import.*from.*['"];?\s*$/gm;
|
231
|
-
const matches = [...content.matchAll(importRegex)];
|
232
|
-
if (matches.length > 0) {
|
233
|
-
const lastImport = matches[matches.length - 1];
|
234
|
-
const insertIndex = lastImport.index! + lastImport[0].length;
|
235
|
-
content = content.slice(0, insertIndex) + '\n' + importStatement + content.slice(insertIndex);
|
236
|
-
}
|
237
|
-
}
|
238
|
-
|
239
|
-
// Add feature initialization
|
240
|
-
const featureInit = `// Initialize ${feature.name} feature
|
241
|
-
const ${feature.name}Feature = new ${this.capitalizeFirst(feature.name)}Feature();
|
242
|
-
app.use('/api/${feature.name}', ${feature.name}Feature.getRouter());`;
|
243
|
-
|
244
|
-
if (!content.includes(featureInit)) {
|
245
|
-
// Find where routes are registered and add after
|
246
|
-
const routeRegex = /app\.use\(.*\);?\s*$/gm;
|
247
|
-
const matches = [...content.matchAll(routeRegex)];
|
248
|
-
if (matches.length > 0) {
|
249
|
-
const lastRoute = matches[matches.length - 1];
|
250
|
-
const insertIndex = lastRoute.index! + lastRoute[0].length;
|
251
|
-
content = content.slice(0, insertIndex) + '\n' + featureInit + content.slice(insertIndex);
|
252
|
-
}
|
253
|
-
}
|
254
|
-
|
255
|
-
await fs.writeFile(filePath, content);
|
256
|
-
console.log(chalk.green(` ✅ Updated ${serverFile}`));
|
257
|
-
break; // Only update the first found server file
|
258
|
-
}
|
259
|
-
}
|
260
|
-
}
|
261
|
-
|
262
|
-
private async updateRoutesIndex(projectPath: string, feature: Feature): Promise<void> {
|
263
|
-
const routesIndexPath = path.join(projectPath, 'src/routes/index.ts');
|
264
|
-
|
265
|
-
if (await fs.pathExists(routesIndexPath)) {
|
266
|
-
let content = await fs.readFile(routesIndexPath, 'utf8');
|
267
|
-
|
268
|
-
// Add import for feature routes
|
269
|
-
const importStatement = `import ${feature.name}Routes from '../features/${feature.name}/routes';`;
|
270
|
-
|
271
|
-
if (!content.includes(importStatement)) {
|
272
|
-
const importRegex = /import.*from.*['"];?\s*$/gm;
|
273
|
-
const matches = [...content.matchAll(importRegex)];
|
274
|
-
if (matches.length > 0) {
|
275
|
-
const lastImport = matches[matches.length - 1];
|
276
|
-
const insertIndex = lastImport.index! + lastImport[0].length;
|
277
|
-
content = content.slice(0, insertIndex) + '\n' + importStatement + content.slice(insertIndex);
|
278
|
-
}
|
279
|
-
}
|
280
|
-
|
281
|
-
// Add route registration
|
282
|
-
const routeRegistration = `app.use('/api/${feature.name}', ${feature.name}Routes);`;
|
283
|
-
|
284
|
-
if (!content.includes(routeRegistration)) {
|
285
|
-
const routeRegex = /app\.use\(.*\);?\s*$/gm;
|
286
|
-
const matches = [...content.matchAll(routeRegex)];
|
287
|
-
if (matches.length > 0) {
|
288
|
-
const lastRoute = matches[matches.length - 1];
|
289
|
-
const insertIndex = lastRoute.index! + lastRoute[0].length;
|
290
|
-
content = content.slice(0, insertIndex) + '\n' + routeRegistration + content.slice(insertIndex);
|
291
|
-
}
|
292
|
-
}
|
293
|
-
|
294
|
-
await fs.writeFile(routesIndexPath, content);
|
295
|
-
console.log(chalk.green(` ✅ Updated routes/index.ts`));
|
296
|
-
}
|
297
|
-
}
|
298
|
-
|
299
|
-
private async updatePackageJson(projectPath: string, feature: Feature): Promise<void> {
|
300
|
-
const packageJsonPath = path.join(projectPath, 'package.json');
|
301
|
-
|
302
|
-
if (await fs.pathExists(packageJsonPath)) {
|
303
|
-
const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
|
304
|
-
|
305
|
-
// Add feature-specific scripts if needed
|
306
|
-
if (!packageJson.scripts) {
|
307
|
-
packageJson.scripts = {};
|
308
|
-
}
|
309
|
-
|
310
|
-
// Add feature-specific scripts
|
311
|
-
const featureScripts = {
|
312
|
-
[`test:${feature.name}`]: `npm test -- --grep "${feature.name}"`,
|
313
|
-
[`build:${feature.name}`]: `npm run build && echo "Built with ${feature.name} feature"`
|
314
|
-
};
|
315
|
-
|
316
|
-
for (const [scriptName, scriptCommand] of Object.entries(featureScripts)) {
|
317
|
-
if (!packageJson.scripts[scriptName]) {
|
318
|
-
packageJson.scripts[scriptName] = scriptCommand;
|
319
|
-
}
|
320
|
-
}
|
321
|
-
|
322
|
-
await fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
323
|
-
console.log(chalk.green(` ✅ Updated package.json with ${feature.name} scripts`));
|
324
|
-
}
|
325
|
-
}
|
326
|
-
|
327
|
-
private capitalizeFirst(str: string): string {
|
328
|
-
return str.charAt(0).toUpperCase() + str.slice(1);
|
329
|
-
}
|
330
|
-
}
|
@@ -1,335 +0,0 @@
|
|
1
|
-
import * as fs from 'fs-extra';
|
2
|
-
import * as path from 'path';
|
3
|
-
import * as glob from 'glob';
|
4
|
-
import chalk from 'chalk';
|
5
|
-
import { execSync } from 'child_process';
|
6
|
-
|
7
|
-
export interface InitOptions {
|
8
|
-
template?: string;
|
9
|
-
yes?: boolean;
|
10
|
-
}
|
11
|
-
|
12
|
-
export class ProjectInitializer {
|
13
|
-
private templateFiles: string[] = [
|
14
|
-
'scripts',
|
15
|
-
'src/**/*',
|
16
|
-
'scripts/**/*',
|
17
|
-
'package.json',
|
18
|
-
'tsconfig.json',
|
19
|
-
'webpack.config.js',
|
20
|
-
'main.ts',
|
21
|
-
'certs/**/*'
|
22
|
-
];
|
23
|
-
|
24
|
-
constructor() {
|
25
|
-
// No need for template path as we'll use npm package
|
26
|
-
}
|
27
|
-
|
28
|
-
async initialize(projectName: string, options: InitOptions): Promise<void> {
|
29
|
-
const projectPath = path.resolve(process.cwd(), projectName);
|
30
|
-
const templateType = options.template || 'api';
|
31
|
-
|
32
|
-
// Check if project directory already exists
|
33
|
-
if (await fs.pathExists(projectPath)) {
|
34
|
-
throw new Error(`Project directory ${projectName} already exists`);
|
35
|
-
}
|
36
|
-
|
37
|
-
// Create project directory
|
38
|
-
await fs.ensureDir(projectPath);
|
39
|
-
|
40
|
-
// Install the appropriate template package
|
41
|
-
await this.installTemplatePackage(projectPath, templateType);
|
42
|
-
|
43
|
-
// Copy template files from the installed package
|
44
|
-
await this.copyTemplateFiles(projectPath, templateType);
|
45
|
-
|
46
|
-
// Update package.json with project name
|
47
|
-
await this.updatePackageJson(projectPath, projectName, templateType);
|
48
|
-
|
49
|
-
// Create .gitignore
|
50
|
-
await this.createGitignore(projectPath);
|
51
|
-
|
52
|
-
// Create README
|
53
|
-
await this.createReadme(projectPath, projectName, templateType);
|
54
|
-
|
55
|
-
// Make scripts executable
|
56
|
-
await this.makeScriptsExecutable(projectPath);
|
57
|
-
|
58
|
-
// Process template for project name (update database names, etc.)
|
59
|
-
await this.processTemplate(projectPath, projectName);
|
60
|
-
|
61
|
-
// Clean up the temporary installation
|
62
|
-
await this.cleanup(projectPath);
|
63
|
-
|
64
|
-
console.log(chalk.green(`✅ Project structure created successfully`));
|
65
|
-
}
|
66
|
-
|
67
|
-
private async installTemplatePackage(projectPath: string, templateType: string): Promise<void> {
|
68
|
-
const packageName = `@diagramers/${templateType}`;
|
69
|
-
console.log(chalk.blue(`📦 Installing ${packageName} template...`));
|
70
|
-
|
71
|
-
try {
|
72
|
-
// Install the template package temporarily
|
73
|
-
execSync(`npm install ${packageName} --no-save`, {
|
74
|
-
cwd: projectPath,
|
75
|
-
stdio: 'inherit'
|
76
|
-
});
|
77
|
-
} catch (error: any) {
|
78
|
-
throw new Error(`Failed to install ${packageName} package: ${error.message}`);
|
79
|
-
}
|
80
|
-
}
|
81
|
-
|
82
|
-
private async copyTemplateFiles(projectPath: string, templateType: string): Promise<void> {
|
83
|
-
const nodeModulesPath = path.join(projectPath, 'node_modules', '@diagramers', templateType);
|
84
|
-
|
85
|
-
if (!await fs.pathExists(nodeModulesPath)) {
|
86
|
-
throw new Error(`@diagramers/${templateType} package not found in node_modules`);
|
87
|
-
}
|
88
|
-
|
89
|
-
for (const pattern of this.templateFiles) {
|
90
|
-
try {
|
91
|
-
const files = glob.sync(pattern, {
|
92
|
-
cwd: nodeModulesPath,
|
93
|
-
ignore: ['node_modules/**', 'dist/**', '.git/**'],
|
94
|
-
nodir: false
|
95
|
-
});
|
96
|
-
|
97
|
-
for (const file of files) {
|
98
|
-
const sourcePath = path.join(nodeModulesPath, file);
|
99
|
-
const destPath = path.join(projectPath, file);
|
100
|
-
|
101
|
-
if (await fs.pathExists(sourcePath)) {
|
102
|
-
const stat = await fs.stat(sourcePath);
|
103
|
-
|
104
|
-
if (stat.isDirectory()) {
|
105
|
-
// Copy directory
|
106
|
-
await fs.ensureDir(destPath);
|
107
|
-
await fs.copy(sourcePath, destPath);
|
108
|
-
} else {
|
109
|
-
// Copy file
|
110
|
-
await fs.ensureDir(path.dirname(destPath));
|
111
|
-
await fs.copy(sourcePath, destPath);
|
112
|
-
}
|
113
|
-
}
|
114
|
-
}
|
115
|
-
} catch (error: any) {
|
116
|
-
// Log warning but continue with other files
|
117
|
-
console.log(chalk.yellow(`⚠️ Warning: Could not copy pattern '${pattern}': ${error.message}`));
|
118
|
-
}
|
119
|
-
}
|
120
|
-
}
|
121
|
-
|
122
|
-
private async cleanup(projectPath: string): Promise<void> {
|
123
|
-
// Remove the temporary node_modules
|
124
|
-
const nodeModulesPath = path.join(projectPath, 'node_modules');
|
125
|
-
if (await fs.pathExists(nodeModulesPath)) {
|
126
|
-
await fs.remove(nodeModulesPath);
|
127
|
-
}
|
128
|
-
|
129
|
-
// Remove package-lock.json if it exists
|
130
|
-
const packageLockPath = path.join(projectPath, 'package-lock.json');
|
131
|
-
if (await fs.pathExists(packageLockPath)) {
|
132
|
-
await fs.remove(packageLockPath);
|
133
|
-
}
|
134
|
-
}
|
135
|
-
|
136
|
-
private async updatePackageJson(projectPath: string, projectName: string, templateType: string): Promise<void> {
|
137
|
-
const packageJsonPath = path.join(projectPath, 'package.json');
|
138
|
-
|
139
|
-
if (await fs.pathExists(packageJsonPath)) {
|
140
|
-
const packageJson = await fs.readJson(packageJsonPath);
|
141
|
-
packageJson.name = projectName;
|
142
|
-
packageJson.description = `${templateType.toUpperCase()} project: ${projectName}`;
|
143
|
-
|
144
|
-
// Remove the bin field if it exists (this is for CLI packages)
|
145
|
-
if (packageJson.bin) {
|
146
|
-
delete packageJson.bin;
|
147
|
-
}
|
148
|
-
|
149
|
-
// Ensure CLI scripts are included
|
150
|
-
if (!packageJson.scripts) {
|
151
|
-
packageJson.scripts = {};
|
152
|
-
}
|
153
|
-
|
154
|
-
// Add CLI scripts if they don't exist
|
155
|
-
if (!packageJson.scripts['generate:module']) {
|
156
|
-
packageJson.scripts['generate:module'] = './scripts/generate-module.sh';
|
157
|
-
}
|
158
|
-
if (!packageJson.scripts['cli']) {
|
159
|
-
packageJson.scripts['cli'] = './scripts/cli-commands.sh';
|
160
|
-
}
|
161
|
-
|
162
|
-
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
163
|
-
}
|
164
|
-
}
|
165
|
-
|
166
|
-
private async createGitignore(projectPath: string): Promise<void> {
|
167
|
-
const gitignoreContent = `
|
168
|
-
# Dependencies
|
169
|
-
node_modules/
|
170
|
-
npm-debug.log*
|
171
|
-
yarn-debug.log*
|
172
|
-
yarn-error.log*
|
173
|
-
|
174
|
-
# Build outputs
|
175
|
-
dist/
|
176
|
-
build/
|
177
|
-
*.tsbuildinfo
|
178
|
-
|
179
|
-
# Environment variables
|
180
|
-
.env
|
181
|
-
.env.local
|
182
|
-
.env.development.local
|
183
|
-
.env.test.local
|
184
|
-
.env.production.local
|
185
|
-
|
186
|
-
# IDE
|
187
|
-
.vscode/
|
188
|
-
.idea/
|
189
|
-
*.swp
|
190
|
-
*.swo
|
191
|
-
|
192
|
-
# OS
|
193
|
-
.DS_Store
|
194
|
-
Thumbs.db
|
195
|
-
|
196
|
-
# Logs
|
197
|
-
logs
|
198
|
-
*.log
|
199
|
-
|
200
|
-
# Runtime data
|
201
|
-
pids
|
202
|
-
*.pid
|
203
|
-
*.seed
|
204
|
-
*.pid.lock
|
205
|
-
|
206
|
-
# Coverage directory used by tools like istanbul
|
207
|
-
coverage/
|
208
|
-
|
209
|
-
# Firebase
|
210
|
-
.firebase/
|
211
|
-
firebase-debug.log
|
212
|
-
firestore-debug.log
|
213
|
-
ui-debug.log
|
214
|
-
|
215
|
-
# Temporary folders
|
216
|
-
tmp/
|
217
|
-
temp/
|
218
|
-
`.trim();
|
219
|
-
|
220
|
-
await fs.writeFile(path.join(projectPath, '.gitignore'), gitignoreContent);
|
221
|
-
}
|
222
|
-
|
223
|
-
private async createReadme(projectPath: string, projectName: string, templateType: string): Promise<void> {
|
224
|
-
const templateTitle = templateType.toUpperCase();
|
225
|
-
const readmeContent = `# ${projectName}
|
226
|
-
|
227
|
-
A Node.js ${templateType} project built with TypeScript and Firebase Functions.
|
228
|
-
|
229
|
-
## Features
|
230
|
-
|
231
|
-
- Express.js server with TypeScript
|
232
|
-
- Firebase Functions integration
|
233
|
-
- Socket.io for real-time communication
|
234
|
-
- MongoDB with Mongoose
|
235
|
-
- Authentication system
|
236
|
-
- Email notifications
|
237
|
-
- Cron jobs
|
238
|
-
- Audit logging
|
239
|
-
|
240
|
-
## Getting Started
|
241
|
-
|
242
|
-
1. Install dependencies:
|
243
|
-
\`\`\`bash
|
244
|
-
npm install
|
245
|
-
\`\`\`
|
246
|
-
|
247
|
-
2. Set up environment variables:
|
248
|
-
- Copy \`.env.example\` to \`.env\`
|
249
|
-
- Update the values according to your configuration
|
250
|
-
|
251
|
-
3. Start development server:
|
252
|
-
\`\`\`bash
|
253
|
-
npm run serve
|
254
|
-
\`\`\`
|
255
|
-
|
256
|
-
## Available Scripts
|
257
|
-
|
258
|
-
- \`npm run serve\` - Start development server
|
259
|
-
- \`npm run build:dev\` - Build for development
|
260
|
-
- \`npm run build:prod\` - Build for production
|
261
|
-
- \`npm run deploy\` - Deploy to Firebase
|
262
|
-
|
263
|
-
## Project Structure
|
264
|
-
|
265
|
-
\`\`\`
|
266
|
-
src/
|
267
|
-
├── config/ # Configuration files
|
268
|
-
├── controllers/ # Route controllers
|
269
|
-
├── entities/ # Database entities
|
270
|
-
├── helpers/ # Utility functions
|
271
|
-
├── routes/ # API routes
|
272
|
-
├── schemas/ # Validation schemas
|
273
|
-
├── server/ # Server setup
|
274
|
-
└── services/ # Business logic
|
275
|
-
\`\`\`
|
276
|
-
|
277
|
-
## Contributing
|
278
|
-
|
279
|
-
1. Fork the repository
|
280
|
-
2. Create a feature branch
|
281
|
-
3. Make your changes
|
282
|
-
4. Test thoroughly
|
283
|
-
5. Submit a pull request
|
284
|
-
|
285
|
-
## License
|
286
|
-
|
287
|
-
MIT
|
288
|
-
`;
|
289
|
-
|
290
|
-
await fs.writeFile(path.join(projectPath, 'README.md'), readmeContent);
|
291
|
-
}
|
292
|
-
|
293
|
-
private async makeScriptsExecutable(projectPath: string): Promise<void> {
|
294
|
-
const scriptsDir = path.join(projectPath, 'scripts');
|
295
|
-
|
296
|
-
if (await fs.pathExists(scriptsDir)) {
|
297
|
-
try {
|
298
|
-
const scripts = await fs.readdir(scriptsDir);
|
299
|
-
for (const script of scripts) {
|
300
|
-
if (script.endsWith('.sh')) {
|
301
|
-
const scriptPath = path.join(scriptsDir, script);
|
302
|
-
// Make script executable (chmod 755)
|
303
|
-
await fs.chmod(scriptPath, 0o755);
|
304
|
-
console.log(chalk.blue(`🔧 Made executable: ${script}`));
|
305
|
-
}
|
306
|
-
}
|
307
|
-
} catch (error: any) {
|
308
|
-
console.log(chalk.yellow(`⚠️ Warning: Could not make scripts executable: ${error.message}`));
|
309
|
-
}
|
310
|
-
}
|
311
|
-
}
|
312
|
-
|
313
|
-
private async processTemplate(projectPath: string, projectName: string): Promise<void> {
|
314
|
-
try {
|
315
|
-
console.log(chalk.blue(`🔧 Processing template for project: ${projectName}`));
|
316
|
-
|
317
|
-
// Change to project directory
|
318
|
-
const originalCwd = process.cwd();
|
319
|
-
process.chdir(projectPath);
|
320
|
-
|
321
|
-
// Run the template processor
|
322
|
-
execSync(`npm run cli process:template ${projectName}`, {
|
323
|
-
stdio: 'inherit'
|
324
|
-
});
|
325
|
-
|
326
|
-
// Change back to original directory
|
327
|
-
process.chdir(originalCwd);
|
328
|
-
|
329
|
-
console.log(chalk.green(`✅ Template processed successfully`));
|
330
|
-
} catch (error: any) {
|
331
|
-
console.log(chalk.yellow(`⚠️ Warning: Could not process template: ${error.message}`));
|
332
|
-
console.log(chalk.yellow(` You can manually run: npm run cli process:template ${projectName}`));
|
333
|
-
}
|
334
|
-
}
|
335
|
-
}
|