create-backlist 4.0.0 → 5.0.1
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/bin/index.js +71 -15
- package/package.json +5 -3
- package/src/generators/dotnet.js +117 -36
- package/src/generators/java.js +100 -0
- package/src/generators/node.js +122 -70
- package/src/generators/python.js +75 -0
- package/src/templates/java-spring/partials/Controller.java.ejs +61 -0
- package/src/templates/java-spring/partials/Entity.java.ejs +24 -0
- package/src/templates/java-spring/partials/Repository.java.ejs +12 -0
- package/src/templates/node-ts-express/partials/ApiDocs.ts.ejs +33 -0
- package/src/templates/node-ts-express/partials/App.test.ts.ejs +38 -0
- package/src/templates/node-ts-express/partials/Controller.ts.ejs +124 -63
- package/src/templates/node-ts-express/partials/DbContext.cs.ejs +15 -0
- package/src/templates/node-ts-express/partials/Dockerfile.ejs +33 -0
- package/src/templates/node-ts-express/partials/Model.cs.ejs +18 -0
- package/src/templates/node-ts-express/partials/PrismaController.ts.ejs +66 -0
- package/src/templates/node-ts-express/partials/PrismaSchema.prisma.ejs +28 -0
- package/src/templates/node-ts-express/partials/docker-compose.yml.ejs +47 -0
- package/src/templates/python-fastapi/main.py.ejs +38 -0
- package/src/templates/python-fastapi/requirements.txt.ejs +4 -0
- package/src/templates/python-fastapi/routes.py.ejs +42 -0
package/bin/index.js
CHANGED
|
@@ -11,7 +11,7 @@ const { generateNodeProject } = require('../src/generators/node');
|
|
|
11
11
|
const { generateDotnetProject } = require('../src/generators/dotnet');
|
|
12
12
|
|
|
13
13
|
async function main() {
|
|
14
|
-
console.log(chalk.cyan.bold('🚀 Welcome to Backlist! The
|
|
14
|
+
console.log(chalk.cyan.bold('🚀 Welcome to Backlist! The Production-Ready Backend Generator.'));
|
|
15
15
|
|
|
16
16
|
const answers = await inquirer.prompt([
|
|
17
17
|
{
|
|
@@ -29,37 +29,91 @@ async function main() {
|
|
|
29
29
|
{ name: 'Node.js (TypeScript, Express)', value: 'node-ts-express' },
|
|
30
30
|
{ name: 'C# (ASP.NET Core Web API)', value: 'dotnet-webapi' },
|
|
31
31
|
new inquirer.Separator(),
|
|
32
|
-
{ name: 'Python (FastAPI) - Coming Soon', disabled: true },
|
|
33
|
-
{ name: 'Java (Spring Boot) - Coming Soon', disabled: true },
|
|
32
|
+
{ name: 'Python (FastAPI) - Coming Soon', disabled: true, value: 'python-fastapi' },
|
|
33
|
+
{ name: 'Java (Spring Boot) - Coming Soon', disabled: true, value: 'java-spring' },
|
|
34
34
|
],
|
|
35
35
|
},
|
|
36
|
+
// --- V5.0: Database Choice for Node.js ---
|
|
37
|
+
{
|
|
38
|
+
type: 'list',
|
|
39
|
+
name: 'dbType',
|
|
40
|
+
message: 'Select your database type:',
|
|
41
|
+
choices: [
|
|
42
|
+
{ name: 'NoSQL (MongoDB with Mongoose)', value: 'mongoose' },
|
|
43
|
+
{ name: 'SQL (PostgreSQL/MySQL with Prisma)', value: 'prisma' },
|
|
44
|
+
],
|
|
45
|
+
when: (answers) => answers.stack === 'node-ts-express'
|
|
46
|
+
},
|
|
36
47
|
{
|
|
37
48
|
type: 'input',
|
|
38
49
|
name: 'srcPath',
|
|
39
50
|
message: 'Enter the path to your frontend `src` directory:',
|
|
40
51
|
default: 'src',
|
|
41
52
|
},
|
|
53
|
+
// --- V3.0: Auth Boilerplate for Node.js ---
|
|
42
54
|
{
|
|
43
55
|
type: 'confirm',
|
|
44
56
|
name: 'addAuth',
|
|
45
|
-
message: '
|
|
57
|
+
message: 'Add JWT authentication boilerplate?',
|
|
46
58
|
default: true,
|
|
47
59
|
when: (answers) => answers.stack === 'node-ts-express'
|
|
48
60
|
},
|
|
49
|
-
// ---
|
|
61
|
+
// --- V4.0: Seeder for Node.js ---
|
|
50
62
|
{
|
|
51
63
|
type: 'confirm',
|
|
52
64
|
name: 'addSeeder',
|
|
53
|
-
message: '
|
|
65
|
+
message: 'Add a database seeder with sample data?',
|
|
54
66
|
default: true,
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
67
|
+
when: (answers) => answers.addAuth // Seeder is useful when there's an auth/user model
|
|
68
|
+
},
|
|
69
|
+
// --- V5.0: Extra Features for Node.js ---
|
|
70
|
+
{
|
|
71
|
+
type: 'checkbox',
|
|
72
|
+
name: 'extraFeatures',
|
|
73
|
+
message: 'Select additional features to include:',
|
|
74
|
+
choices: [
|
|
75
|
+
{ name: 'Docker Support (Dockerfile & docker-compose.yml)', value: 'docker', checked: true },
|
|
76
|
+
{ name: 'API Testing Boilerplate (Jest & Supertest)', value: 'testing' },
|
|
77
|
+
{ name: 'API Documentation (Swagger UI)', value: 'swagger' },
|
|
78
|
+
],
|
|
79
|
+
when: (answers) => answers.stack === 'node-ts-express'
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
type: 'list',
|
|
83
|
+
name: 'dbType',
|
|
84
|
+
message: 'Select your database type:',
|
|
85
|
+
choices: [
|
|
86
|
+
{ name: 'NoSQL (MongoDB with Mongoose)', value: 'mongoose' },
|
|
87
|
+
{ name: 'SQL (PostgreSQL/MySQL with Prisma)', value: 'prisma' },
|
|
88
|
+
],
|
|
89
|
+
when: (answers) => answers.stack === 'node-ts-express'
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
type: 'checkbox',
|
|
93
|
+
name: 'extraFeatures',
|
|
94
|
+
message: 'Select additional features to include:',
|
|
95
|
+
choices: [
|
|
96
|
+
{ name: 'Docker Support (Dockerfile & docker-compose.yml)', value: 'docker', checked: true },
|
|
97
|
+
// ... other features
|
|
98
|
+
],
|
|
99
|
+
when: (answers) => answers.stack === 'node-ts-express'
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
type: 'list',
|
|
103
|
+
name: 'stack',
|
|
104
|
+
message: 'Select the backend stack:',
|
|
105
|
+
choices: [
|
|
106
|
+
{ name: 'Node.js (TypeScript, Express)', value: 'node-ts-express' },
|
|
107
|
+
{ name: 'C# (ASP.NET Core Web API)', value: 'dotnet-webapi' },
|
|
108
|
+
new inquirer.Separator(),
|
|
109
|
+
{ name: 'Python (FastAPI) - Coming Soon', disabled: true, value: 'python-fastapi' },
|
|
110
|
+
{ name: 'Java (Spring Boot)', value: 'java-spring' }, // <-- ENABLED!
|
|
111
|
+
],
|
|
112
|
+
},
|
|
58
113
|
]);
|
|
59
114
|
|
|
60
|
-
// Pass all answers to the options object
|
|
61
115
|
const options = {
|
|
62
|
-
...answers,
|
|
116
|
+
...answers,
|
|
63
117
|
projectDir: path.resolve(process.cwd(), answers.projectName),
|
|
64
118
|
frontendSrcDir: path.resolve(process.cwd(), answers.srcPath),
|
|
65
119
|
};
|
|
@@ -70,18 +124,19 @@ async function main() {
|
|
|
70
124
|
// --- Dispatcher Logic ---
|
|
71
125
|
switch (options.stack) {
|
|
72
126
|
case 'node-ts-express':
|
|
73
|
-
await generateNodeProject(options);
|
|
127
|
+
await generateNodeProject(options);
|
|
74
128
|
break;
|
|
75
129
|
|
|
76
130
|
case 'dotnet-webapi':
|
|
77
131
|
if (!await isCommandAvailable('dotnet')) {
|
|
78
132
|
throw new Error('.NET SDK is not installed. Please install it from https://dotnet.microsoft.com/download');
|
|
79
133
|
}
|
|
134
|
+
// Note: The dotnet generator currently only supports basic route generation (v1.0 features).
|
|
80
135
|
await generateDotnetProject(options);
|
|
81
136
|
break;
|
|
82
137
|
|
|
83
138
|
default:
|
|
84
|
-
throw new Error(`The selected stack '${options.stack}' is not supported.`);
|
|
139
|
+
throw new Error(`The selected stack '${options.stack}' is not supported yet.`);
|
|
85
140
|
}
|
|
86
141
|
|
|
87
142
|
console.log(chalk.green.bold('\n✅ Backend generation complete!'));
|
|
@@ -90,8 +145,9 @@ async function main() {
|
|
|
90
145
|
console.log(chalk.cyan(' (Check the generated README.md for instructions)'));
|
|
91
146
|
|
|
92
147
|
} catch (error) {
|
|
93
|
-
console.error(chalk.red.bold('\n❌ An error occurred:'));
|
|
94
|
-
|
|
148
|
+
console.error(chalk.red.bold('\n❌ An error occurred during generation:'));
|
|
149
|
+
// Make sure we print the full error for debugging
|
|
150
|
+
console.error(error);
|
|
95
151
|
|
|
96
152
|
if (fs.existsSync(options.projectDir)) {
|
|
97
153
|
console.log(chalk.yellow(' -> Cleaning up failed installation...'));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-backlist",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.1",
|
|
4
4
|
"description": "An advanced, multi-language backend generator based on frontend analysis.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"bin": {
|
|
@@ -18,11 +18,13 @@
|
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"@babel/parser": "^7.22.7",
|
|
20
20
|
"@babel/traverse": "^7.22.8",
|
|
21
|
+
"axios": "^1.13.1",
|
|
21
22
|
"chalk": "^4.1.2",
|
|
22
23
|
"ejs": "^3.1.9",
|
|
23
24
|
"execa": "^6.1.0",
|
|
24
25
|
"fs-extra": "^11.1.1",
|
|
25
26
|
"glob": "^10.3.3",
|
|
26
|
-
"inquirer": "^8.2.4"
|
|
27
|
+
"inquirer": "^8.2.4",
|
|
28
|
+
"unzipper": "^0.12.3"
|
|
27
29
|
}
|
|
28
|
-
}
|
|
30
|
+
}
|
package/src/generators/dotnet.js
CHANGED
|
@@ -8,45 +8,126 @@ const { renderAndWrite, getTemplatePath } = require('./template');
|
|
|
8
8
|
async function generateDotnetProject(options) {
|
|
9
9
|
const { projectDir, projectName, frontendSrcDir } = options;
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
console.log(chalk.blue(' -> Scaffolding .NET Core Web API project...'));
|
|
26
|
-
await execa('dotnet', ['new', 'webapi', '-n', projectName, '-o', projectDir, '--no-https']);
|
|
27
|
-
|
|
28
|
-
await fs.remove(path.join(projectDir, 'Controllers', 'WeatherForecastController.cs'));
|
|
29
|
-
await fs.remove(path.join(projectDir, 'WeatherForecast.cs'));
|
|
30
|
-
|
|
31
|
-
console.log(chalk.blue(' -> Generating custom controllers...'));
|
|
32
|
-
for (const controllerName of Object.keys(controllers)) {
|
|
33
|
-
if (controllerName === 'Default') continue; // Skip if no proper controller name was found
|
|
34
|
-
await renderAndWrite(
|
|
35
|
-
getTemplatePath('dotnet/partials/Controller.cs.ejs'),
|
|
36
|
-
path.join(projectDir, 'Controllers', `${controllerName}Controller.cs`),
|
|
37
|
-
{
|
|
38
|
-
projectName,
|
|
39
|
-
controllerName,
|
|
40
|
-
endpoints: controllers[controllerName]
|
|
11
|
+
try {
|
|
12
|
+
// --- Step 1: Analysis & Model Identification ---
|
|
13
|
+
console.log(chalk.blue(' -> Analyzing frontend for C# backend...'));
|
|
14
|
+
const endpoints = await analyzeFrontend(frontendSrcDir);
|
|
15
|
+
const modelsToGenerate = new Map();
|
|
16
|
+
endpoints.forEach(ep => {
|
|
17
|
+
// For C#, we create a model if schemaFields exist for any endpoint related to a controller
|
|
18
|
+
if (ep.schemaFields && ep.controllerName !== 'Default' && !modelsToGenerate.has(ep.controllerName)) {
|
|
19
|
+
modelsToGenerate.set(ep.controllerName, {
|
|
20
|
+
name: ep.controllerName,
|
|
21
|
+
fields: Object.entries(ep.schemaFields).map(([key, type]) => ({ name: key, type }))
|
|
22
|
+
});
|
|
41
23
|
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
if (modelsToGenerate.size > 0) {
|
|
27
|
+
console.log(chalk.green(` -> Identified ${modelsToGenerate.size} models/controllers to generate.`));
|
|
28
|
+
} else {
|
|
29
|
+
console.log(chalk.yellow(' -> No API calls with body data found. A basic API project will be created without models.'));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// --- Step 2: Create Base .NET Project using `dotnet new` ---
|
|
33
|
+
console.log(chalk.blue(' -> Scaffolding .NET Core Web API project...'));
|
|
34
|
+
await execa('dotnet', ['new', 'webapi', '-n', projectName, '-o', projectDir, '--no-https']);
|
|
35
|
+
|
|
36
|
+
// --- Step 3: Add Required NuGet Packages ---
|
|
37
|
+
if (modelsToGenerate.size > 0) {
|
|
38
|
+
console.log(chalk.blue(' -> Adding NuGet packages (Entity Framework Core)...'));
|
|
39
|
+
const packages = [
|
|
40
|
+
'Microsoft.EntityFrameworkCore.Design',
|
|
41
|
+
'Microsoft.EntityFrameworkCore.InMemory' // Using InMemory for a simple, runnable setup
|
|
42
|
+
// For a real DB, a user would add: 'Npgsql.EntityFrameworkCore.PostgreSQL' or 'Microsoft.EntityFrameworkCore.SqlServer'
|
|
43
|
+
];
|
|
44
|
+
for (const pkg of packages) {
|
|
45
|
+
await execa('dotnet', ['add', 'package', pkg], { cwd: projectDir });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// --- Step 4: Generate Models and DbContext from Templates ---
|
|
50
|
+
if (modelsToGenerate.size > 0) {
|
|
51
|
+
console.log(chalk.blue(' -> Generating EF Core models and DbContext...'));
|
|
52
|
+
const modelsDir = path.join(projectDir, 'Models');
|
|
53
|
+
const dataDir = path.join(projectDir, 'Data');
|
|
54
|
+
await fs.ensureDir(modelsDir);
|
|
55
|
+
await fs.ensureDir(dataDir);
|
|
56
|
+
|
|
57
|
+
for (const [modelName, modelData] of modelsToGenerate.entries()) {
|
|
58
|
+
await renderAndWrite(
|
|
59
|
+
getTemplatePath('dotnet/partials/Model.cs.ejs'),
|
|
60
|
+
path.join(modelsDir, `${modelName}.cs`),
|
|
61
|
+
{ projectName, modelName, model: modelData }
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
await renderAndWrite(
|
|
66
|
+
getTemplatePath('dotnet/partials/DbContext.cs.ejs'),
|
|
67
|
+
path.join(dataDir, 'ApplicationDbContext.cs'),
|
|
68
|
+
{ projectName, modelsToGenerate: Array.from(modelsToGenerate.values()) }
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// --- Step 5: Configure Services in Program.cs ---
|
|
73
|
+
console.log(chalk.blue(' -> Configuring services in Program.cs...'));
|
|
74
|
+
const programCsPath = path.join(projectDir, 'Program.cs');
|
|
75
|
+
let programCsContent = await fs.readFile(programCsPath, 'utf-8');
|
|
76
|
+
|
|
77
|
+
let usingStatements = 'using Microsoft.EntityFrameworkCore;\nusing '+projectName+'.Data;\n';
|
|
78
|
+
programCsContent = usingStatements + programCsContent;
|
|
79
|
+
|
|
80
|
+
let dbContextService = `// Configure the database context\nbuilder.Services.AddDbContext<ApplicationDbContext>(opt => opt.UseInMemoryDatabase("MyDb"));`;
|
|
81
|
+
programCsContent = programCsContent.replace('builder.Services.AddControllers();', `builder.Services.AddControllers();\n\n${dbContextService}`);
|
|
82
|
+
|
|
83
|
+
// Enable CORS to allow frontend communication
|
|
84
|
+
const corsPolicy = `
|
|
85
|
+
builder.Services.AddCors(options =>
|
|
86
|
+
{
|
|
87
|
+
options.AddDefaultPolicy(
|
|
88
|
+
policy =>
|
|
89
|
+
{
|
|
90
|
+
policy.WithOrigins("http://localhost:3000", "http://localhost:5173") // Common frontend dev ports
|
|
91
|
+
.AllowAnyHeader()
|
|
92
|
+
.AllowAnyMethod();
|
|
93
|
+
});
|
|
94
|
+
});`;
|
|
95
|
+
programCsContent = programCsContent.replace('var app = builder.Build();', `${corsPolicy}\n\nvar app = builder.Build();\n\napp.UseCors();`);
|
|
96
|
+
|
|
97
|
+
await fs.writeFile(programCsPath, programCsContent);
|
|
98
|
+
|
|
99
|
+
// --- Step 6: Generate Controllers with full CRUD ---
|
|
100
|
+
console.log(chalk.blue(' -> Generating controllers with CRUD logic...'));
|
|
101
|
+
await fs.remove(path.join(projectDir, 'Controllers', 'WeatherForecastController.cs'));
|
|
102
|
+
await fs.remove(path.join(projectDir, 'WeatherForecast.cs'));
|
|
103
|
+
|
|
104
|
+
const controllersToGenerate = new Set(Array.from(modelsToGenerate.keys()));
|
|
105
|
+
// Also add controllers for endpoints that didn't have a body but were detected
|
|
106
|
+
endpoints.forEach(ep => {
|
|
107
|
+
if (ep.controllerName !== 'Default') controllersToGenerate.add(ep.controllerName);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
for (const controllerName of controllersToGenerate) {
|
|
111
|
+
await renderAndWrite(
|
|
112
|
+
getTemplatePath('dotnet/partials/Controller.cs.ejs'),
|
|
113
|
+
path.join(projectDir, 'Controllers', `${controllerName}Controller.cs`),
|
|
114
|
+
{ projectName, controllerName }
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// --- Step 7: Generate README ---
|
|
119
|
+
await renderAndWrite(
|
|
120
|
+
getTemplatePath('dotnet/partials/README.md.ejs'),
|
|
121
|
+
path.join(projectDir, 'README.md'),
|
|
122
|
+
{ projectName }
|
|
42
123
|
);
|
|
43
|
-
}
|
|
44
124
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
125
|
+
console.log(chalk.green(' -> C# backend generation is complete!'));
|
|
126
|
+
|
|
127
|
+
} catch (error) {
|
|
128
|
+
// Re-throw the error to be caught by the main CLI handler
|
|
129
|
+
throw error;
|
|
130
|
+
}
|
|
50
131
|
}
|
|
51
132
|
|
|
52
133
|
module.exports = { generateDotnetProject };
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const { execa } = require('execa');
|
|
3
|
+
const fs = require('fs-extra');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const axios = require('axios'); // For making HTTP requests
|
|
6
|
+
const unzipper = require('unzipper'); // For extracting .zip files
|
|
7
|
+
const { analyzeFrontend } = require('../analyzer');
|
|
8
|
+
const { renderAndWrite, getTemplatePath } = require('./template');
|
|
9
|
+
|
|
10
|
+
async function generateJavaProject(options) {
|
|
11
|
+
const { projectDir, projectName, frontendSrcDir } = options;
|
|
12
|
+
const group = 'com.backlist.generated'; // A default Java group ID
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
// --- Step 1: Download Base Project from Spring Initializr ---
|
|
16
|
+
console.log(chalk.blue(' -> Contacting Spring Initializr to download a base Spring Boot project...'));
|
|
17
|
+
|
|
18
|
+
// Define standard dependencies for a web API
|
|
19
|
+
const dependencies = 'web,data-jpa,lombok,postgresql'; // Using PostgreSQL as an example DB driver
|
|
20
|
+
const springInitializrUrl = `https://start.spring.io/starter.zip?type=maven-project&language=java&bootVersion=3.2.0&groupId=${group}&artifactId=${projectName}&name=${projectName}&dependencies=${dependencies}`;
|
|
21
|
+
|
|
22
|
+
const response = await axios({
|
|
23
|
+
url: springInitializrUrl,
|
|
24
|
+
method: 'GET',
|
|
25
|
+
responseType: 'stream'
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// --- Step 2: Unzip the Downloaded Project ---
|
|
29
|
+
console.log(chalk.blue(' -> Unzipping the Spring Boot project...'));
|
|
30
|
+
await new Promise((resolve, reject) => {
|
|
31
|
+
const stream = response.data.pipe(unzipper.Extract({ path: projectDir }));
|
|
32
|
+
stream.on('finish', () => {
|
|
33
|
+
console.log(chalk.gray(' -> Project unzipped successfully.'));
|
|
34
|
+
resolve();
|
|
35
|
+
});
|
|
36
|
+
stream.on('error', reject);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// --- Step 3: Analyze Frontend ---
|
|
40
|
+
const endpoints = await analyzeFrontend(frontendSrcDir);
|
|
41
|
+
const modelsToGenerate = new Map();
|
|
42
|
+
endpoints.forEach(ep => {
|
|
43
|
+
if (ep.schemaFields && ep.controllerName !== 'Default' && !modelsToGenerate.has(ep.controllerName)) {
|
|
44
|
+
modelsToGenerate.set(ep.controllerName, { name: ep.controllerName, fields: Object.entries(ep.schemaFields).map(([key, type]) => ({ name: key, type })) });
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// --- Step 4: Generate Java Entities and Controllers ---
|
|
49
|
+
if (modelsToGenerate.size > 0) {
|
|
50
|
+
console.log(chalk.blue(' -> Generating Java entities and controllers...'));
|
|
51
|
+
|
|
52
|
+
const javaSrcPath = path.join(projectDir, 'src', 'main', 'java', ...group.split('.'), projectName);
|
|
53
|
+
const entityDir = path.join(javaSrcPath, 'model');
|
|
54
|
+
const controllerDir = path.join(javaSrcPath, 'controller');
|
|
55
|
+
await fs.ensureDir(entityDir);
|
|
56
|
+
await fs.ensureDir(controllerDir);
|
|
57
|
+
|
|
58
|
+
for (const [modelName, modelData] of modelsToGenerate.entries()) {
|
|
59
|
+
await renderAndWrite(
|
|
60
|
+
getTemplatePath('java-spring/partials/Entity.java.ejs'),
|
|
61
|
+
path.join(entityDir, `${modelName}.java`),
|
|
62
|
+
{ group, projectName, modelName, model: modelData }
|
|
63
|
+
);
|
|
64
|
+
await renderAndWrite(
|
|
65
|
+
getTemplatePath('java-spring/partials/Controller.java.ejs'),
|
|
66
|
+
path.join(controllerDir, `${modelName}Controller.java`),
|
|
67
|
+
{ group, projectName, controllerName: modelName }
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// --- Step 5: Configure application.properties ---
|
|
73
|
+
console.log(chalk.blue(' -> Configuring database in application.properties...'));
|
|
74
|
+
const propsPath = path.join(projectDir, 'src', 'main', 'resources', 'application.properties');
|
|
75
|
+
const dbProps = [
|
|
76
|
+
`\n\n# --- Auto-generated by create-backlist ---`,
|
|
77
|
+
`# --- Database Configuration (PostgreSQL) ---`,
|
|
78
|
+
`spring.datasource.url=jdbc:postgresql://localhost:5432/${projectName}`,
|
|
79
|
+
`spring.datasource.username=postgres`,
|
|
80
|
+
`spring.datasource.password=password`,
|
|
81
|
+
`spring.jpa.hibernate.ddl-auto=update`,
|
|
82
|
+
`spring.jpa.show-sql=true`,
|
|
83
|
+
];
|
|
84
|
+
await fs.appendFile(propsPath, dbProps.join('\n'));
|
|
85
|
+
|
|
86
|
+
console.log(chalk.green(' -> Java (Spring Boot) backend generation is complete!'));
|
|
87
|
+
console.log(chalk.yellow('\nTo run your new Java backend:'));
|
|
88
|
+
console.log(chalk.cyan(' 1. Open the project in a Java IDE (like IntelliJ IDEA or VS Code).'));
|
|
89
|
+
console.log(chalk.cyan(' 2. Run the main application file.'));
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
} catch (error) {
|
|
93
|
+
if (error.response) {
|
|
94
|
+
throw new Error(`Failed to download from Spring Initializr. Status: ${error.response.status}`);
|
|
95
|
+
}
|
|
96
|
+
throw error;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
module.exports = { generateJavaProject };
|