create-backlist 7.3.1 → 7.4.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 (44) hide show
  1. package/bin/index.js +483 -470
  2. package/bin/qa.js +103 -0
  3. package/package.json +7 -3
  4. package/src/env-resolver.js +70 -70
  5. package/src/generators/dotnet.js +134 -134
  6. package/src/generators/java.js +248 -248
  7. package/src/generators/js.js +345 -345
  8. package/src/generators/nestjs.js +277 -277
  9. package/src/generators/python.js +86 -86
  10. package/src/project-detector.js +131 -131
  11. package/src/qa/qa-engine.js +909 -0
  12. package/src/templates/dotnet/partials/Dockerfile.ejs +27 -27
  13. package/src/templates/dotnet/partials/docker-compose.yml.ejs +33 -33
  14. package/src/templates/js-express/base/server.js +59 -59
  15. package/src/templates/js-express/partials/Dockerfile.ejs +12 -12
  16. package/src/templates/js-express/partials/auth.controller.js.ejs +66 -66
  17. package/src/templates/js-express/partials/auth.middleware.js.ejs +19 -19
  18. package/src/templates/js-express/partials/auth.routes.js.ejs +9 -9
  19. package/src/templates/js-express/partials/controller.js.ejs +53 -53
  20. package/src/templates/js-express/partials/db.js.ejs +19 -19
  21. package/src/templates/js-express/partials/docker-compose.yml.ejs +46 -46
  22. package/src/templates/js-express/partials/model.js.ejs +18 -18
  23. package/src/templates/js-express/partials/package.json.ejs +17 -17
  24. package/src/templates/js-express/partials/prisma.schema.ejs +21 -21
  25. package/src/templates/js-express/partials/routes.js.ejs +19 -19
  26. package/src/templates/js-express/partials/seeder.js.ejs +103 -103
  27. package/src/templates/js-express/partials/service.js.ejs +51 -51
  28. package/src/templates/js-express/partials/swagger.js.ejs +30 -30
  29. package/src/templates/js-express/partials/test.js.ejs +46 -46
  30. package/src/templates/nestjs/base/app.module.ts +9 -9
  31. package/src/templates/nestjs/base/main.ts +23 -23
  32. package/src/templates/nestjs/base/tsconfig.json +21 -21
  33. package/src/templates/nestjs/partials/auth.controller.ts.ejs +17 -17
  34. package/src/templates/nestjs/partials/auth.module.ts.ejs +17 -17
  35. package/src/templates/nestjs/partials/auth.service.ts.ejs +70 -70
  36. package/src/templates/nestjs/partials/controller.ts.ejs +34 -34
  37. package/src/templates/nestjs/partials/create-dto.ts.ejs +22 -22
  38. package/src/templates/nestjs/partials/jwt-guard.ts.ejs +24 -24
  39. package/src/templates/nestjs/partials/module.ts.ejs +10 -10
  40. package/src/templates/nestjs/partials/package.json.ejs +27 -27
  41. package/src/templates/nestjs/partials/prisma.service.ts.ejs +13 -13
  42. package/src/templates/nestjs/partials/schema.ts.ejs +19 -19
  43. package/src/templates/nestjs/partials/service.ts.ejs +67 -67
  44. package/src/templates/nestjs/partials/update-dto.ts.ejs +4 -4
package/bin/qa.js ADDED
@@ -0,0 +1,103 @@
1
+ // ═══════════════════════════════════════════════════════════════════════════
2
+ // Backlist QA CLI — qa.js
3
+ // Standalone QA entry point: `node bin/qa.js`
4
+ // Copyright (c) W.A.H.ISHAN — MIT License
5
+ // ═══════════════════════════════════════════════════════════════════════════
6
+
7
+ #!/usr/bin/env node
8
+
9
+ import * as p from '@clack/prompts';
10
+ import chalk from 'chalk';
11
+ import { runManualQA, runAutomatedQA, viewQAHistory, initQASystem } from '../src/qa/qa-engine.js';
12
+
13
+ // ── QA Banner ────────────────────────────────────────────────────────────
14
+ function printQABanner() {
15
+ const c1 = chalk.hex('#00F5FF');
16
+ const c2 = chalk.hex('#BF40FF');
17
+ const c3 = chalk.hex('#FF6B6B');
18
+ const dim = chalk.gray;
19
+
20
+ console.log('');
21
+ console.log(c1(' ╔══════════════════════════════════════════════════════════════╗'));
22
+ console.log(c1(' ║') + c2.bold(' ____ ___ ______ ____ ______________ _ ________ ') + c1('║'));
23
+ console.log(c1(' ║') + c2.bold(' / __ )/ | / ____// //_// / _/ ___/ / | / / ____/ ') + c1('║'));
24
+ console.log(c1(' ║') + c2.bold(' / __ / /| | / / / ,< / // /\\__ \\ / |/ / / __ ') + c1('║'));
25
+ console.log(c1(' ║') + c2.bold('/ /_/ / ___ |/ /___ / /| |/ /___/__ /_ / /| / /_/ / ') + c1('║'));
26
+ console.log(c1(' ║') + c2.bold('/_____/_/ |_|\\____//_/ |_/_//____/___//_/ |_/\\____/ ') + c1('║'));
27
+ console.log(c1(' ║') + ' ' + c1('║'));
28
+ console.log(c1(' ║') + c3.bold(' 🧪 QA Testing System — Manual & Automated 🤖 ') + c1('║'));
29
+ console.log(c1(' ║') + dim(' Bug Reports · Test Suites · HTML/JSON Export ') + c1('║'));
30
+ console.log(c1(' ╚══════════════════════════════════════════════════════════════╝'));
31
+ console.log('');
32
+ console.log(dim(' Part of create-backlist v7.0 — Polyglot Backend Engine'));
33
+ console.log(dim(' ─────────────────────────────────────────────────────────────'));
34
+ console.log('');
35
+ }
36
+
37
+ // ── Main QA CLI ──────────────────────────────────────────────────────────
38
+ async function main() {
39
+ printQABanner();
40
+ await initQASystem();
41
+
42
+ p.intro(chalk.hex('#00F5FF').bold(' Backlist QA — Testing & Quality Assurance '));
43
+
44
+ const mode = await p.select({
45
+ message: 'Select QA mode:',
46
+ options: [
47
+ {
48
+ value: 'manual',
49
+ label: '🧪 Manual QA Testing',
50
+ hint: 'Interactive — create, execute & track test cases with bug reports',
51
+ },
52
+ {
53
+ value: 'auto',
54
+ label: '🤖 Automated QA Testing',
55
+ hint: 'One-time run — test suites against your project',
56
+ },
57
+ {
58
+ value: 'auto-continuous',
59
+ label: '⚡ Live Continuous Automated QA',
60
+ hint: 'Runs every 30s — real-time monitoring',
61
+ },
62
+ {
63
+ value: 'history',
64
+ label: '📜 View QA History',
65
+ hint: 'See past QA run summaries',
66
+ },
67
+ ],
68
+ });
69
+
70
+ if (p.isCancel(mode)) {
71
+ p.cancel('QA session cancelled.');
72
+ process.exit(0);
73
+ }
74
+
75
+ switch (mode) {
76
+ case 'manual':
77
+ await runManualQA();
78
+ break;
79
+
80
+ case 'auto':
81
+ await runAutomatedQA({ continuous: false });
82
+ break;
83
+
84
+ case 'auto-continuous':
85
+ await runAutomatedQA({ continuous: true });
86
+ break;
87
+
88
+ case 'history':
89
+ await viewQAHistory();
90
+ p.outro(chalk.hex('#00F5FF').bold('History viewed.'));
91
+ break;
92
+
93
+ default:
94
+ p.cancel('Unknown mode.');
95
+ process.exit(1);
96
+ }
97
+ }
98
+
99
+ main().catch((err) => {
100
+ console.error(chalk.red.bold(`\n QA Error: ${err.message || err}`));
101
+ if (err.stack) console.error(chalk.gray(err.stack));
102
+ process.exit(1);
103
+ });
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "create-backlist",
3
- "version": "7.3.1",
3
+ "version": "7.4.0",
4
4
  "description": "An advanced, multi-language backend generator based on frontend analysis. Smart Freemium SaaS CLI.",
5
5
  "type": "module",
6
6
  "bin": {
7
- "create-backlist": "bin/index.js"
7
+ "create-backlist": "bin/index.js",
8
+ "backlist-qa": "bin/qa.js"
8
9
  },
9
10
  "files": [
10
11
  "bin",
@@ -12,7 +13,10 @@
12
13
  "AiModuls"
13
14
  ],
14
15
  "scripts": {
15
- "start": "node bin/index.js"
16
+ "start": "node bin/index.js",
17
+ "qa": "node bin/qa.js",
18
+ "qa:auto": "node bin/qa.js --auto",
19
+ "qa:live": "node bin/qa.js --live"
16
20
  },
17
21
  "author": "W.A.H.ISHAN",
18
22
  "license": "MIT",
@@ -1,70 +1,70 @@
1
- import fs from "fs-extra";
2
- import path from "node:path";
3
-
4
- const ENV_FILES = [
5
- ".env.local",
6
- ".env.development",
7
- ".env.development.local",
8
- ".env",
9
- ];
10
-
11
- const API_URL_PATTERNS = [
12
- "API_URL",
13
- "API_BASE_URL",
14
- "API_BASE",
15
- "BASE_URL",
16
- "BACKEND_URL",
17
- "SERVER_URL",
18
- ];
19
-
20
- export async function parseEnvFiles(projectRoot) {
21
- const envMap = new Map();
22
-
23
- for (const envFile of ENV_FILES) {
24
- const filePath = path.join(projectRoot, envFile);
25
- if (!(await fs.pathExists(filePath))) continue;
26
-
27
- let content;
28
- try {
29
- content = await fs.readFile(filePath, "utf-8");
30
- } catch {
31
- continue;
32
- }
33
-
34
- for (const line of content.split("\n")) {
35
- const trimmed = line.trim();
36
- if (!trimmed || trimmed.startsWith("#")) continue;
37
-
38
- const eqIndex = trimmed.indexOf("=");
39
- if (eqIndex === -1) continue;
40
-
41
- const key = trimmed.slice(0, eqIndex).trim();
42
- let value = trimmed.slice(eqIndex + 1).trim();
43
-
44
- if (
45
- (value.startsWith('"') && value.endsWith('"')) ||
46
- (value.startsWith("'") && value.endsWith("'"))
47
- ) {
48
- value = value.slice(1, -1);
49
- }
50
-
51
- if (key && value && !envMap.has(key)) {
52
- envMap.set(key, value);
53
- }
54
- }
55
- }
56
-
57
- return envMap;
58
- }
59
-
60
- export function extractBaseUrlFromEnv(envMap) {
61
- for (const [key, value] of envMap) {
62
- const upperKey = key.toUpperCase();
63
- for (const pattern of API_URL_PATTERNS) {
64
- if (upperKey.includes(pattern) && value.startsWith("http")) {
65
- return value.replace(/\/$/, "");
66
- }
67
- }
68
- }
69
- return null;
70
- }
1
+ import fs from "fs-extra";
2
+ import path from "node:path";
3
+
4
+ const ENV_FILES = [
5
+ ".env.local",
6
+ ".env.development",
7
+ ".env.development.local",
8
+ ".env",
9
+ ];
10
+
11
+ const API_URL_PATTERNS = [
12
+ "API_URL",
13
+ "API_BASE_URL",
14
+ "API_BASE",
15
+ "BASE_URL",
16
+ "BACKEND_URL",
17
+ "SERVER_URL",
18
+ ];
19
+
20
+ export async function parseEnvFiles(projectRoot) {
21
+ const envMap = new Map();
22
+
23
+ for (const envFile of ENV_FILES) {
24
+ const filePath = path.join(projectRoot, envFile);
25
+ if (!(await fs.pathExists(filePath))) continue;
26
+
27
+ let content;
28
+ try {
29
+ content = await fs.readFile(filePath, "utf-8");
30
+ } catch {
31
+ continue;
32
+ }
33
+
34
+ for (const line of content.split("\n")) {
35
+ const trimmed = line.trim();
36
+ if (!trimmed || trimmed.startsWith("#")) continue;
37
+
38
+ const eqIndex = trimmed.indexOf("=");
39
+ if (eqIndex === -1) continue;
40
+
41
+ const key = trimmed.slice(0, eqIndex).trim();
42
+ let value = trimmed.slice(eqIndex + 1).trim();
43
+
44
+ if (
45
+ (value.startsWith('"') && value.endsWith('"')) ||
46
+ (value.startsWith("'") && value.endsWith("'"))
47
+ ) {
48
+ value = value.slice(1, -1);
49
+ }
50
+
51
+ if (key && value && !envMap.has(key)) {
52
+ envMap.set(key, value);
53
+ }
54
+ }
55
+ }
56
+
57
+ return envMap;
58
+ }
59
+
60
+ export function extractBaseUrlFromEnv(envMap) {
61
+ for (const [key, value] of envMap) {
62
+ const upperKey = key.toUpperCase();
63
+ for (const pattern of API_URL_PATTERNS) {
64
+ if (upperKey.includes(pattern) && value.startsWith("http")) {
65
+ return value.replace(/\/$/, "");
66
+ }
67
+ }
68
+ }
69
+ return null;
70
+ }
@@ -1,134 +1,134 @@
1
- import chalk from 'chalk';
2
- import { execa } from 'execa';
3
- import fs from 'fs-extra';
4
- import path from 'node:path';
5
- import { analyzeFrontend } from '../analyzer.js';
6
- import { renderAndWrite, getTemplatePath } from './template.js';
7
-
8
- export async function generateDotnetProject(options) {
9
- const { projectDir, projectName, frontendSrcDir } = options;
10
-
11
- try {
12
- console.log(chalk.blue(' -> Analyzing frontend for C# backend...'));
13
- const endpoints = await analyzeFrontend(frontendSrcDir);
14
- const modelsToGenerate = new Map();
15
- endpoints.forEach(ep => {
16
- if (ep.schemaFields && ep.controllerName !== 'Default' && !modelsToGenerate.has(ep.controllerName)) {
17
- modelsToGenerate.set(ep.controllerName, {
18
- name: ep.controllerName,
19
- fields: Object.entries(ep.schemaFields).map(([key, type]) => ({ name: key, type }))
20
- });
21
- }
22
- });
23
-
24
- if (modelsToGenerate.size > 0) {
25
- console.log(chalk.green(` -> Identified ${modelsToGenerate.size} models/controllers to generate.`));
26
- } else {
27
- console.log(chalk.yellow(' -> No API calls with body data found. A basic API project will be created without models.'));
28
- }
29
-
30
- console.log(chalk.blue(' -> Scaffolding .NET Core Web API project...'));
31
- await execa('dotnet', ['new', 'webapi', '-n', projectName, '-o', projectDir, '--no-https']);
32
-
33
- if (modelsToGenerate.size > 0) {
34
- console.log(chalk.blue(' -> Adding NuGet packages (Entity Framework Core)...'));
35
- const packages = [
36
- 'Microsoft.EntityFrameworkCore.Design',
37
- 'Microsoft.EntityFrameworkCore.InMemory'
38
- ];
39
- for (const pkg of packages) {
40
- await execa('dotnet', ['add', 'package', pkg], { cwd: projectDir });
41
- }
42
- }
43
-
44
- if (modelsToGenerate.size > 0) {
45
- console.log(chalk.blue(' -> Generating EF Core models and DbContext...'));
46
- const modelsDir = path.join(projectDir, 'Models');
47
- const dataDir = path.join(projectDir, 'Data');
48
- await fs.ensureDir(modelsDir);
49
- await fs.ensureDir(dataDir);
50
-
51
- for (const [modelName, modelData] of modelsToGenerate.entries()) {
52
- await renderAndWrite(
53
- getTemplatePath('dotnet/partials/Model.cs.ejs'),
54
- path.join(modelsDir, `${modelName}.cs`),
55
- { projectName, modelName, model: modelData }
56
- );
57
- }
58
-
59
- await renderAndWrite(
60
- getTemplatePath('dotnet/partials/DbContext.cs.ejs'),
61
- path.join(dataDir, 'ApplicationDbContext.cs'),
62
- { projectName, modelsToGenerate: Array.from(modelsToGenerate.values()) }
63
- );
64
- }
65
-
66
- console.log(chalk.blue(' -> Configuring services in Program.cs...'));
67
- const programCsPath = path.join(projectDir, 'Program.cs');
68
- let programCsContent = await fs.readFile(programCsPath, 'utf-8');
69
-
70
- let usingStatements = 'using Microsoft.EntityFrameworkCore;\nusing ' + projectName + '.Data;\n';
71
- programCsContent = usingStatements + programCsContent;
72
-
73
- let dbContextService = '// Configure the database context\nbuilder.Services.AddDbContext<ApplicationDbContext>(opt => opt.UseInMemoryDatabase("MyDb"));';
74
- programCsContent = programCsContent.replace('builder.Services.AddControllers();', `builder.Services.AddControllers();\n\n${dbContextService}`);
75
-
76
- const corsPolicy = `
77
- builder.Services.AddCors(options =>
78
- {
79
- options.AddDefaultPolicy(
80
- policy =>
81
- {
82
- policy.WithOrigins("http://localhost:3000", "http://localhost:5173")
83
- .AllowAnyHeader()
84
- .AllowAnyMethod();
85
- });
86
- });`;
87
- programCsContent = programCsContent.replace('var app = builder.Build();', `${corsPolicy}\n\nvar app = builder.Build();\n\napp.UseCors();`);
88
-
89
- await fs.writeFile(programCsPath, programCsContent);
90
-
91
- console.log(chalk.blue(' -> Generating controllers with CRUD logic...'));
92
- await fs.remove(path.join(projectDir, 'Controllers', 'WeatherForecastController.cs'));
93
- await fs.remove(path.join(projectDir, 'WeatherForecast.cs'));
94
-
95
- const controllersToGenerate = new Set(Array.from(modelsToGenerate.keys()));
96
- endpoints.forEach(ep => {
97
- if (ep.controllerName !== 'Default') controllersToGenerate.add(ep.controllerName);
98
- });
99
-
100
- for (const controllerName of controllersToGenerate) {
101
- const controllerEndpoints = endpoints.filter(
102
- ep => ep.controllerName === controllerName
103
- );
104
- await renderAndWrite(
105
- getTemplatePath('dotnet/partials/Controller.cs.ejs'),
106
- path.join(projectDir, 'Controllers', `${controllerName}Controller.cs`),
107
- { projectName, controllerName, endpoints: controllerEndpoints }
108
- );
109
- }
110
-
111
- console.log(chalk.blue(' -> Generating Docker files...'));
112
- await renderAndWrite(
113
- getTemplatePath('dotnet/partials/Dockerfile.ejs'),
114
- path.join(projectDir, 'Dockerfile'),
115
- { projectName }
116
- );
117
- await renderAndWrite(
118
- getTemplatePath('dotnet/partials/docker-compose.yml.ejs'),
119
- path.join(projectDir, 'docker-compose.yml'),
120
- { projectName }
121
- );
122
-
123
- await renderAndWrite(
124
- getTemplatePath('dotnet/partials/README.md.ejs'),
125
- path.join(projectDir, 'README.md'),
126
- { projectName }
127
- );
128
-
129
- console.log(chalk.green(' -> C# backend generation is complete!'));
130
-
131
- } catch (error) {
132
- throw error;
133
- }
134
- }
1
+ import chalk from 'chalk';
2
+ import { execa } from 'execa';
3
+ import fs from 'fs-extra';
4
+ import path from 'node:path';
5
+ import { analyzeFrontend } from '../analyzer.js';
6
+ import { renderAndWrite, getTemplatePath } from './template.js';
7
+
8
+ export async function generateDotnetProject(options) {
9
+ const { projectDir, projectName, frontendSrcDir } = options;
10
+
11
+ try {
12
+ console.log(chalk.blue(' -> Analyzing frontend for C# backend...'));
13
+ const endpoints = await analyzeFrontend(frontendSrcDir);
14
+ const modelsToGenerate = new Map();
15
+ endpoints.forEach(ep => {
16
+ if (ep.schemaFields && ep.controllerName !== 'Default' && !modelsToGenerate.has(ep.controllerName)) {
17
+ modelsToGenerate.set(ep.controllerName, {
18
+ name: ep.controllerName,
19
+ fields: Object.entries(ep.schemaFields).map(([key, type]) => ({ name: key, type }))
20
+ });
21
+ }
22
+ });
23
+
24
+ if (modelsToGenerate.size > 0) {
25
+ console.log(chalk.green(` -> Identified ${modelsToGenerate.size} models/controllers to generate.`));
26
+ } else {
27
+ console.log(chalk.yellow(' -> No API calls with body data found. A basic API project will be created without models.'));
28
+ }
29
+
30
+ console.log(chalk.blue(' -> Scaffolding .NET Core Web API project...'));
31
+ await execa('dotnet', ['new', 'webapi', '-n', projectName, '-o', projectDir, '--no-https']);
32
+
33
+ if (modelsToGenerate.size > 0) {
34
+ console.log(chalk.blue(' -> Adding NuGet packages (Entity Framework Core)...'));
35
+ const packages = [
36
+ 'Microsoft.EntityFrameworkCore.Design',
37
+ 'Microsoft.EntityFrameworkCore.InMemory'
38
+ ];
39
+ for (const pkg of packages) {
40
+ await execa('dotnet', ['add', 'package', pkg], { cwd: projectDir });
41
+ }
42
+ }
43
+
44
+ if (modelsToGenerate.size > 0) {
45
+ console.log(chalk.blue(' -> Generating EF Core models and DbContext...'));
46
+ const modelsDir = path.join(projectDir, 'Models');
47
+ const dataDir = path.join(projectDir, 'Data');
48
+ await fs.ensureDir(modelsDir);
49
+ await fs.ensureDir(dataDir);
50
+
51
+ for (const [modelName, modelData] of modelsToGenerate.entries()) {
52
+ await renderAndWrite(
53
+ getTemplatePath('dotnet/partials/Model.cs.ejs'),
54
+ path.join(modelsDir, `${modelName}.cs`),
55
+ { projectName, modelName, model: modelData }
56
+ );
57
+ }
58
+
59
+ await renderAndWrite(
60
+ getTemplatePath('dotnet/partials/DbContext.cs.ejs'),
61
+ path.join(dataDir, 'ApplicationDbContext.cs'),
62
+ { projectName, modelsToGenerate: Array.from(modelsToGenerate.values()) }
63
+ );
64
+ }
65
+
66
+ console.log(chalk.blue(' -> Configuring services in Program.cs...'));
67
+ const programCsPath = path.join(projectDir, 'Program.cs');
68
+ let programCsContent = await fs.readFile(programCsPath, 'utf-8');
69
+
70
+ let usingStatements = 'using Microsoft.EntityFrameworkCore;\nusing ' + projectName + '.Data;\n';
71
+ programCsContent = usingStatements + programCsContent;
72
+
73
+ let dbContextService = '// Configure the database context\nbuilder.Services.AddDbContext<ApplicationDbContext>(opt => opt.UseInMemoryDatabase("MyDb"));';
74
+ programCsContent = programCsContent.replace('builder.Services.AddControllers();', `builder.Services.AddControllers();\n\n${dbContextService}`);
75
+
76
+ const corsPolicy = `
77
+ builder.Services.AddCors(options =>
78
+ {
79
+ options.AddDefaultPolicy(
80
+ policy =>
81
+ {
82
+ policy.WithOrigins("http://localhost:3000", "http://localhost:5173")
83
+ .AllowAnyHeader()
84
+ .AllowAnyMethod();
85
+ });
86
+ });`;
87
+ programCsContent = programCsContent.replace('var app = builder.Build();', `${corsPolicy}\n\nvar app = builder.Build();\n\napp.UseCors();`);
88
+
89
+ await fs.writeFile(programCsPath, programCsContent);
90
+
91
+ console.log(chalk.blue(' -> Generating controllers with CRUD logic...'));
92
+ await fs.remove(path.join(projectDir, 'Controllers', 'WeatherForecastController.cs'));
93
+ await fs.remove(path.join(projectDir, 'WeatherForecast.cs'));
94
+
95
+ const controllersToGenerate = new Set(Array.from(modelsToGenerate.keys()));
96
+ endpoints.forEach(ep => {
97
+ if (ep.controllerName !== 'Default') controllersToGenerate.add(ep.controllerName);
98
+ });
99
+
100
+ for (const controllerName of controllersToGenerate) {
101
+ const controllerEndpoints = endpoints.filter(
102
+ ep => ep.controllerName === controllerName
103
+ );
104
+ await renderAndWrite(
105
+ getTemplatePath('dotnet/partials/Controller.cs.ejs'),
106
+ path.join(projectDir, 'Controllers', `${controllerName}Controller.cs`),
107
+ { projectName, controllerName, endpoints: controllerEndpoints }
108
+ );
109
+ }
110
+
111
+ console.log(chalk.blue(' -> Generating Docker files...'));
112
+ await renderAndWrite(
113
+ getTemplatePath('dotnet/partials/Dockerfile.ejs'),
114
+ path.join(projectDir, 'Dockerfile'),
115
+ { projectName }
116
+ );
117
+ await renderAndWrite(
118
+ getTemplatePath('dotnet/partials/docker-compose.yml.ejs'),
119
+ path.join(projectDir, 'docker-compose.yml'),
120
+ { projectName }
121
+ );
122
+
123
+ await renderAndWrite(
124
+ getTemplatePath('dotnet/partials/README.md.ejs'),
125
+ path.join(projectDir, 'README.md'),
126
+ { projectName }
127
+ );
128
+
129
+ console.log(chalk.green(' -> C# backend generation is complete!'));
130
+
131
+ } catch (error) {
132
+ throw error;
133
+ }
134
+ }