create-backlist 6.0.7 → 6.0.8
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 +141 -0
- package/package.json +4 -10
- package/src/analyzer.js +104 -315
- package/src/generators/dotnet.js +94 -120
- package/src/generators/java.js +109 -157
- package/src/generators/node.js +85 -262
- package/src/generators/template.js +2 -38
- package/src/templates/dotnet/partials/Controller.cs.ejs +14 -7
- package/src/templates/java-spring/partials/ApplicationSeeder.java.ejs +2 -7
- package/src/templates/java-spring/partials/AuthController.java.ejs +10 -23
- package/src/templates/java-spring/partials/Controller.java.ejs +6 -17
- package/src/templates/java-spring/partials/Dockerfile.ejs +1 -6
- package/src/templates/java-spring/partials/Entity.java.ejs +5 -15
- package/src/templates/java-spring/partials/JwtAuthFilter.java.ejs +7 -30
- package/src/templates/java-spring/partials/JwtService.java.ejs +10 -38
- package/src/templates/java-spring/partials/Repository.java.ejs +1 -10
- package/src/templates/java-spring/partials/Service.java.ejs +7 -45
- package/src/templates/java-spring/partials/User.java.ejs +4 -17
- package/src/templates/java-spring/partials/UserDetailsServiceImpl.java.ejs +4 -10
- package/src/templates/java-spring/partials/UserRepository.java.ejs +0 -8
- package/src/templates/java-spring/partials/docker-compose.yml.ejs +8 -16
- package/src/templates/node-ts-express/base/server.ts +6 -13
- package/src/templates/node-ts-express/base/tsconfig.json +3 -13
- package/src/templates/node-ts-express/partials/ApiDocs.ts.ejs +7 -17
- package/src/templates/node-ts-express/partials/App.test.ts.ejs +26 -49
- package/src/templates/node-ts-express/partials/Auth.controller.ts.ejs +62 -56
- package/src/templates/node-ts-express/partials/Auth.middleware.ts.ejs +10 -21
- package/src/templates/node-ts-express/partials/Controller.ts.ejs +40 -40
- package/src/templates/node-ts-express/partials/DbContext.cs.ejs +3 -3
- package/src/templates/node-ts-express/partials/Dockerfile.ejs +11 -9
- package/src/templates/node-ts-express/partials/Model.cs.ejs +7 -25
- package/src/templates/node-ts-express/partials/Model.ts.ejs +12 -20
- package/src/templates/node-ts-express/partials/PrismaController.ts.ejs +55 -72
- package/src/templates/node-ts-express/partials/PrismaSchema.prisma.ejs +12 -27
- package/src/templates/node-ts-express/partials/README.md.ejs +12 -9
- package/src/templates/node-ts-express/partials/Seeder.ts.ejs +64 -44
- package/src/templates/node-ts-express/partials/docker-compose.yml.ejs +16 -31
- package/src/templates/node-ts-express/partials/package.json.ejs +1 -3
- package/src/templates/node-ts-express/partials/routes.ts.ejs +24 -35
- package/src/utils.js +5 -17
- package/bin/backlist.js +0 -228
- package/src/db/prisma.ts +0 -4
- package/src/scanner/analyzeFrontend.js +0 -146
- package/src/scanner/index.js +0 -99
- package/src/templates/dotnet/partials/Dto.cs.ejs +0 -8
- package/src/templates/node-ts-express/partials/prismaClient.ts.ejs +0 -4
|
@@ -1,63 +1,83 @@
|
|
|
1
|
-
// Auto-generated by create-backlist
|
|
1
|
+
// Auto-generated by create-backlist v4.0 on <%= new Date().toISOString() %>
|
|
2
2
|
import mongoose from 'mongoose';
|
|
3
3
|
import dotenv from 'dotenv';
|
|
4
4
|
import { faker } from '@faker-js/faker';
|
|
5
|
-
import chalk from 'chalk';
|
|
5
|
+
import chalk from 'chalk'; // For colorful console logs
|
|
6
6
|
|
|
7
|
+
// Load env vars
|
|
7
8
|
dotenv.config();
|
|
8
9
|
|
|
10
|
+
// We assume a User model exists for seeding.
|
|
11
|
+
// The path is relative to the generated 'backend' project root.
|
|
9
12
|
import User from '../src/models/User.model';
|
|
10
13
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
// --- Connect to DB ---
|
|
15
|
+
const connectDB = async () => {
|
|
16
|
+
try {
|
|
17
|
+
const MONGO_URI = process.env.MONGO_URI || 'mongodb://127.0.0.1:27017/<%= projectName %>';
|
|
18
|
+
if (!MONGO_URI) {
|
|
19
|
+
throw new Error('MONGO_URI is not defined in your .env file');
|
|
20
|
+
}
|
|
21
|
+
await mongoose.connect(MONGO_URI);
|
|
22
|
+
console.log(chalk.green('MongoDB Connected for Seeder...'));
|
|
23
|
+
} catch (err) {
|
|
24
|
+
console.error(chalk.red(`Seeder DB Connection Error: ${err.message}`));
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
15
28
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
29
|
+
// --- Import Data ---
|
|
30
|
+
const importData = async () => {
|
|
31
|
+
try {
|
|
32
|
+
// Clear existing data
|
|
33
|
+
await User.deleteMany();
|
|
19
34
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
35
|
+
const sampleUsers = [];
|
|
36
|
+
const userCount = 10; // Number of sample users to create
|
|
23
37
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
38
|
+
for (let i = 0; i < userCount; i++) {
|
|
39
|
+
sampleUsers.push({
|
|
40
|
+
name: faker.person.fullName(),
|
|
41
|
+
email: faker.internet.email().toLowerCase(),
|
|
42
|
+
password: 'password123', // All sample users will have the same password for easy testing
|
|
43
|
+
});
|
|
44
|
+
}
|
|
27
45
|
|
|
28
|
-
|
|
29
|
-
name: faker.person.fullName(),
|
|
30
|
-
email: faker.internet.email().toLowerCase(),
|
|
31
|
-
password: 'password123',
|
|
32
|
-
}));
|
|
46
|
+
await User.insertMany(sampleUsers);
|
|
33
47
|
|
|
34
|
-
|
|
48
|
+
console.log(chalk.green.bold('✅ Data Imported Successfully!'));
|
|
49
|
+
process.exit();
|
|
50
|
+
} catch (error) {
|
|
51
|
+
console.error(chalk.red(`Error with data import: ${error.message}`));
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
35
55
|
|
|
36
|
-
|
|
37
|
-
|
|
56
|
+
// --- Destroy Data ---
|
|
57
|
+
const destroyData = async () => {
|
|
58
|
+
try {
|
|
59
|
+
await User.deleteMany();
|
|
60
|
+
// If you have other models, you can add them here for destruction
|
|
61
|
+
// e.g., await Product.deleteMany();
|
|
38
62
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
63
|
+
console.log(chalk.red.bold('🔥 Data Destroyed Successfully!'));
|
|
64
|
+
process.exit();
|
|
65
|
+
} catch (error) {
|
|
66
|
+
console.error(chalk.red(`Error with data destruction: ${error.message}`));
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
43
70
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
71
|
+
// --- CLI Logic to run the seeder ---
|
|
72
|
+
const runSeeder = async () => {
|
|
73
|
+
await connectDB();
|
|
47
74
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
} catch (err) {
|
|
54
|
-
console.error(chalk.red(`Seeder error: ${getErrorMessage(err)}`));
|
|
55
|
-
process.exitCode = 1;
|
|
56
|
-
} finally {
|
|
57
|
-
try {
|
|
58
|
-
await mongoose.disconnect();
|
|
59
|
-
} catch {}
|
|
75
|
+
// process.argv[2] will be '-d' if the script is run with `npm run destroy`
|
|
76
|
+
if (process.argv[2] === '-d') {
|
|
77
|
+
await destroyData();
|
|
78
|
+
} else {
|
|
79
|
+
await importData();
|
|
60
80
|
}
|
|
61
|
-
}
|
|
81
|
+
};
|
|
62
82
|
|
|
63
|
-
|
|
83
|
+
runSeeder();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Auto-generated by create-backlist v5.
|
|
1
|
+
# Auto-generated by create-backlist v5.0
|
|
2
2
|
version: '3.8'
|
|
3
3
|
|
|
4
4
|
services:
|
|
@@ -8,55 +8,40 @@ services:
|
|
|
8
8
|
ports:
|
|
9
9
|
- '<%= port %>:<%= port %>'
|
|
10
10
|
environment:
|
|
11
|
-
PORT
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
MONGO_URI: ${MONGO_URI:-mongodb://db:27017/<%= projectName %>}
|
|
15
|
-
<% } else if (dbType === 'prisma') { -%>
|
|
16
|
-
DATABASE_URL: ${DATABASE_URL:-postgresql://${DB_USER:-postgres}:${DB_PASSWORD:-password}@db:5432/${DB_NAME:-<%= projectName %>}?schema=public}
|
|
17
|
-
<% } -%>
|
|
11
|
+
- PORT=<%= port %>
|
|
12
|
+
- DATABASE_URL=${DATABASE_URL}
|
|
13
|
+
- JWT_SECRET=${JWT_SECRET}
|
|
18
14
|
depends_on:
|
|
19
|
-
db
|
|
20
|
-
condition: service_healthy
|
|
15
|
+
- db
|
|
21
16
|
volumes:
|
|
22
17
|
- .:/usr/src/app
|
|
23
18
|
- /usr/src/app/node_modules
|
|
24
19
|
command: npm run dev
|
|
25
20
|
|
|
26
21
|
db:
|
|
27
|
-
<% if (dbType === 'mongoose') {
|
|
28
|
-
image: mongo:
|
|
22
|
+
<% if (dbType === 'mongoose') { %>
|
|
23
|
+
image: mongo:latest
|
|
29
24
|
container_name: <%= projectName %>-mongo-db
|
|
30
25
|
ports:
|
|
31
26
|
- '27017:27017'
|
|
32
27
|
volumes:
|
|
33
28
|
- mongo-data:/data/db
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
interval: 5s
|
|
37
|
-
timeout: 5s
|
|
38
|
-
retries: 20
|
|
39
|
-
<% } else if (dbType === 'prisma') { -%>
|
|
40
|
-
image: postgres:16-alpine
|
|
29
|
+
<% } else if (dbType === 'prisma') { %>
|
|
30
|
+
image: postgres:14-alpine
|
|
41
31
|
container_name: <%= projectName %>-postgres-db
|
|
42
32
|
ports:
|
|
43
33
|
- '5432:5432'
|
|
44
34
|
environment:
|
|
45
|
-
POSTGRES_USER
|
|
46
|
-
POSTGRES_PASSWORD
|
|
47
|
-
POSTGRES_DB
|
|
35
|
+
- POSTGRES_USER=${DB_USER}
|
|
36
|
+
- POSTGRES_PASSWORD=${DB_PASSWORD}
|
|
37
|
+
- POSTGRES_DB=${DB_NAME}
|
|
48
38
|
volumes:
|
|
49
39
|
- postgres-data:/var/lib/postgresql/data
|
|
50
|
-
|
|
51
|
-
test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-postgres} -d ${DB_NAME:-<%= projectName %>}"]
|
|
52
|
-
interval: 5s
|
|
53
|
-
timeout: 5s
|
|
54
|
-
retries: 20
|
|
55
|
-
<% } -%>
|
|
40
|
+
<% } %>
|
|
56
41
|
|
|
57
42
|
volumes:
|
|
58
|
-
<% if (dbType === 'mongoose') {
|
|
43
|
+
<% if (dbType === 'mongoose') { %>
|
|
59
44
|
mongo-data:
|
|
60
|
-
<% } else if (dbType === 'prisma') {
|
|
45
|
+
<% } else if (dbType === 'prisma') { %>
|
|
61
46
|
postgres-data:
|
|
62
|
-
<% }
|
|
47
|
+
<% } %>
|
|
@@ -4,11 +4,9 @@
|
|
|
4
4
|
"private": true,
|
|
5
5
|
"main": "dist/server.js",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"dev": "ts-node-dev --respawn --transpile-only src/server.ts",
|
|
8
7
|
"build": "tsc",
|
|
9
8
|
"start": "node dist/server.js",
|
|
10
|
-
"
|
|
11
|
-
"clean": "rimraf dist"
|
|
9
|
+
"dev": "ts-node-dev --respawn --transpile-only src/server.ts"
|
|
12
10
|
},
|
|
13
11
|
"dependencies": {
|
|
14
12
|
"cors": "^2.8.5",
|
|
@@ -1,67 +1,56 @@
|
|
|
1
1
|
// Auto-generated by create-backlist on <%= new Date().toISOString() %>
|
|
2
2
|
import { Router, Request, Response } from 'express';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Collect controllers safely
|
|
7
|
-
*/
|
|
3
|
+
<%
|
|
4
|
+
// Build unique controller list safely
|
|
8
5
|
const controllers = [];
|
|
9
6
|
if (Array.isArray(endpoints)) {
|
|
10
|
-
endpoints.forEach(ep => {
|
|
7
|
+
endpoints.forEach((ep) => {
|
|
11
8
|
if (ep && ep.controllerName && ep.controllerName !== 'Default' && !controllers.includes(ep.controllerName)) {
|
|
12
9
|
controllers.push(ep.controllerName);
|
|
13
10
|
}
|
|
14
11
|
});
|
|
15
12
|
}
|
|
16
13
|
%>
|
|
17
|
-
|
|
18
14
|
<% controllers.forEach((ctrl) => { %>
|
|
19
15
|
import * as <%= ctrl %>Controller from './controllers/<%= ctrl %>.controller';
|
|
20
16
|
<% }) %>
|
|
21
17
|
|
|
22
|
-
<% if (addAuth) {
|
|
18
|
+
<% if (addAuth) { %>
|
|
23
19
|
import { protect } from './middleware/Auth.middleware';
|
|
24
|
-
<% }
|
|
20
|
+
<% } %>
|
|
25
21
|
|
|
26
22
|
const router = Router();
|
|
27
23
|
|
|
24
|
+
// If no endpoints detected, emit a basic route so file is valid
|
|
28
25
|
<% if (!Array.isArray(endpoints) || endpoints.length === 0) { %>
|
|
29
26
|
router.get('/health', (_req: Request, res: Response) => {
|
|
30
27
|
res.status(200).json({ ok: true, message: 'Auto-generated routes alive' });
|
|
31
28
|
});
|
|
32
29
|
<% } %>
|
|
33
30
|
|
|
34
|
-
<%
|
|
35
|
-
/**
|
|
36
|
-
* Render endpoints
|
|
37
|
-
* Prefer ep.route (normalized) and fallback to ep.path
|
|
38
|
-
*/
|
|
31
|
+
<%
|
|
39
32
|
if (Array.isArray(endpoints)) {
|
|
40
|
-
endpoints.forEach((ep) => {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
const
|
|
46
|
-
|
|
33
|
+
endpoints.forEach((ep) => {
|
|
34
|
+
const rawPath = (ep && ep.path) ? ep.path : '/';
|
|
35
|
+
const expressPath = (rawPath.replace(/^\/api/, '') || '/').replace(/{(\w+)}/g, ':$1');
|
|
36
|
+
const method = ((ep && ep.method) ? ep.method : 'GET').toLowerCase();
|
|
37
|
+
const ctrl = (ep && ep.controllerName) ? ep.controllerName : 'Default';
|
|
38
|
+
const hasId = expressPath.includes(':');
|
|
39
|
+
let handler = '';
|
|
47
40
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
41
|
+
if (ctrl !== 'Default') {
|
|
42
|
+
if (method === 'post' && !hasId) handler = `${ctrl}Controller.create${ctrl}`;
|
|
43
|
+
else if (method === 'get' && !hasId) handler = `${ctrl}Controller.getAll${ctrl}s`;
|
|
44
|
+
else if (method === 'get' && hasId) handler = `${ctrl}Controller.get${ctrl}ById`;
|
|
45
|
+
else if (method === 'put' && hasId) handler = `${ctrl}Controller.update${ctrl}ById`;
|
|
46
|
+
else if (method === 'delete' && hasId) handler = `${ctrl}Controller.delete${ctrl}ById`;
|
|
47
|
+
}
|
|
51
48
|
|
|
52
|
-
const needsProtect = !!addAuth && method
|
|
49
|
+
const needsProtect = !!addAuth && (method === 'post' || method === 'put' || method === 'delete');
|
|
53
50
|
const middleware = needsProtect ? 'protect, ' : '';
|
|
54
|
-
|
|
55
|
-
let handler = '';
|
|
56
|
-
if (ctrl !== 'Default' && action) {
|
|
57
|
-
handler = `${ctrl}Controller.${action}`;
|
|
58
|
-
}
|
|
59
51
|
%>
|
|
60
|
-
router.<%= method %>(
|
|
61
|
-
|
|
62
|
-
<%- middleware %><%- handler || '(req: Request, res: Response) => res.status(501).json({ message: "Not Implemented" })' %>
|
|
63
|
-
);
|
|
64
|
-
<%
|
|
52
|
+
router.<%= method %>('<%- expressPath || "/" %>', <%- middleware %><%- handler || '(req: Request, res: Response) => res.status(501).json({ message: "Not Implemented" })' %>);
|
|
53
|
+
<%
|
|
65
54
|
});
|
|
66
55
|
}
|
|
67
56
|
%>
|
package/src/utils.js
CHANGED
|
@@ -1,24 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
const { execa } = require("execa");
|
|
3
|
-
|
|
4
|
-
const VERSION_ARGS = {
|
|
5
|
-
java: ["-version"],
|
|
6
|
-
python: ["--version"],
|
|
7
|
-
python3: ["--version"],
|
|
8
|
-
node: ["--version"],
|
|
9
|
-
npm: ["--version"],
|
|
10
|
-
dotnet: ["--version"],
|
|
11
|
-
mvn: ["-v"],
|
|
12
|
-
git: ["--version"],
|
|
13
|
-
};
|
|
1
|
+
const { execa } = require('execa');
|
|
14
2
|
|
|
15
3
|
async function isCommandAvailable(command) {
|
|
16
|
-
const args = VERSION_ARGS[command] || ["--version"];
|
|
17
4
|
try {
|
|
18
|
-
|
|
5
|
+
// Using a harmless version command to check for presence
|
|
6
|
+
const checkCommand = command === 'java' ? '-version' : '--version';
|
|
7
|
+
await execa(command, [checkCommand]);
|
|
19
8
|
return true;
|
|
20
|
-
} catch
|
|
21
|
-
if (err && err.code === "ENOENT") return false;
|
|
9
|
+
} catch {
|
|
22
10
|
return false;
|
|
23
11
|
}
|
|
24
12
|
}
|
package/bin/backlist.js
DELETED
|
@@ -1,228 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
3
|
-
|
|
4
|
-
const inquirer = require("inquirer");
|
|
5
|
-
const chalk = require("chalk");
|
|
6
|
-
const fs = require("fs-extra");
|
|
7
|
-
const path = require("path");
|
|
8
|
-
const { Command } = require("commander");
|
|
9
|
-
const chokidar = require("chokidar");
|
|
10
|
-
|
|
11
|
-
// FIX: repo has utils.js at root
|
|
12
|
-
const { isCommandAvailable } = require("../utils");
|
|
13
|
-
|
|
14
|
-
const { generateNodeProject } = require("../src/generators/node");
|
|
15
|
-
const { generateDotnetProject } = require("../src/generators/dotnet");
|
|
16
|
-
const { generateJavaProject } = require("../src/generators/java");
|
|
17
|
-
const { generatePythonProject } = require("../src/generators/python");
|
|
18
|
-
|
|
19
|
-
const { scanFrontend, writeContracts } = require("../src/scanner");
|
|
20
|
-
|
|
21
|
-
function resolveOptionsFromFlags(flags) {
|
|
22
|
-
return {
|
|
23
|
-
projectName: flags.projectName || "backend",
|
|
24
|
-
srcPath: flags.srcPath || "src",
|
|
25
|
-
stack: flags.stack || "node-ts-express",
|
|
26
|
-
dbType: flags.dbType,
|
|
27
|
-
addAuth: flags.addAuth,
|
|
28
|
-
addSeeder: flags.addSeeder,
|
|
29
|
-
extraFeatures: flags.extraFeatures || [],
|
|
30
|
-
projectDir: path.resolve(process.cwd(), flags.projectName || "backend"),
|
|
31
|
-
frontendSrcDir: path.resolve(process.cwd(), flags.srcPath || "src"),
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
async function runGeneration(options, contracts) {
|
|
36
|
-
switch (options.stack) {
|
|
37
|
-
case "node-ts-express":
|
|
38
|
-
await generateNodeProject({ ...options, contracts });
|
|
39
|
-
break;
|
|
40
|
-
|
|
41
|
-
case "dotnet-webapi":
|
|
42
|
-
if (!(await isCommandAvailable("dotnet"))) {
|
|
43
|
-
throw new Error(".NET SDK is not installed. Install: https://dotnet.microsoft.com/download");
|
|
44
|
-
}
|
|
45
|
-
await generateDotnetProject({ ...options, contracts });
|
|
46
|
-
break;
|
|
47
|
-
|
|
48
|
-
case "java-spring":
|
|
49
|
-
if (!(await isCommandAvailable("java"))) {
|
|
50
|
-
throw new Error("Java (JDK 17+) is not installed. Install a JDK to continue.");
|
|
51
|
-
}
|
|
52
|
-
await generateJavaProject({ ...options, contracts });
|
|
53
|
-
break;
|
|
54
|
-
|
|
55
|
-
case "python-fastapi":
|
|
56
|
-
if (!(await isCommandAvailable("python"))) {
|
|
57
|
-
throw new Error("Python is not installed. Please install Python (3.8+) and pip.");
|
|
58
|
-
}
|
|
59
|
-
await generatePythonProject({ ...options, contracts });
|
|
60
|
-
break;
|
|
61
|
-
|
|
62
|
-
default:
|
|
63
|
-
throw new Error(`Unsupported stack '${options.stack}'.`);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
async function interactiveMain() {
|
|
68
|
-
console.log(chalk.cyan.bold("Welcome to Backlist! The Polyglot Backend Generator."));
|
|
69
|
-
|
|
70
|
-
const answers = await inquirer.prompt([
|
|
71
|
-
{
|
|
72
|
-
type: "input",
|
|
73
|
-
name: "projectName",
|
|
74
|
-
message: "Enter a name for your backend directory:",
|
|
75
|
-
default: "backend",
|
|
76
|
-
validate: (input) => (input ? true : "Project name cannot be empty."),
|
|
77
|
-
},
|
|
78
|
-
{
|
|
79
|
-
type: "list",
|
|
80
|
-
name: "stack",
|
|
81
|
-
message: "Select the backend stack:",
|
|
82
|
-
choices: [
|
|
83
|
-
{ name: "Node.js (TypeScript, Express)", value: "node-ts-express" },
|
|
84
|
-
{ name: "C# (ASP.NET Core Web API)", value: "dotnet-webapi" },
|
|
85
|
-
{ name: "Java (Spring Boot)", value: "java-spring" },
|
|
86
|
-
{ name: "Python (FastAPI)", value: "python-fastapi" },
|
|
87
|
-
],
|
|
88
|
-
},
|
|
89
|
-
{
|
|
90
|
-
type: "input",
|
|
91
|
-
name: "srcPath",
|
|
92
|
-
message: "Enter the path to your frontend `src` directory:",
|
|
93
|
-
default: "src",
|
|
94
|
-
},
|
|
95
|
-
{
|
|
96
|
-
type: "list",
|
|
97
|
-
name: "dbType",
|
|
98
|
-
message: "Select your database type for Node.js:",
|
|
99
|
-
choices: [
|
|
100
|
-
{ name: "NoSQL (MongoDB with Mongoose)", value: "mongoose" },
|
|
101
|
-
{ name: "SQL (PostgreSQL/MySQL with Prisma)", value: "prisma" },
|
|
102
|
-
],
|
|
103
|
-
when: (a) => a.stack === "node-ts-express",
|
|
104
|
-
},
|
|
105
|
-
{
|
|
106
|
-
type: "confirm",
|
|
107
|
-
name: "addAuth",
|
|
108
|
-
message: "Add JWT authentication boilerplate?",
|
|
109
|
-
default: true,
|
|
110
|
-
when: (a) => a.stack === "node-ts-express",
|
|
111
|
-
},
|
|
112
|
-
{
|
|
113
|
-
type: "confirm",
|
|
114
|
-
name: "addSeeder",
|
|
115
|
-
message: "Add a database seeder with sample data?",
|
|
116
|
-
default: true,
|
|
117
|
-
when: (a) => a.stack === "node-ts-express" && a.addAuth,
|
|
118
|
-
},
|
|
119
|
-
{
|
|
120
|
-
type: "checkbox",
|
|
121
|
-
name: "extraFeatures",
|
|
122
|
-
message: "Select additional features for Node.js:",
|
|
123
|
-
choices: [
|
|
124
|
-
{ name: "Docker Support", value: "docker", checked: true },
|
|
125
|
-
{ name: "API Testing Boilerplate (Jest & Supertest)", value: "testing", checked: true },
|
|
126
|
-
{ name: "API Documentation (Swagger UI)", value: "swagger", checked: true },
|
|
127
|
-
],
|
|
128
|
-
when: (a) => a.stack === "node-ts-express",
|
|
129
|
-
},
|
|
130
|
-
]);
|
|
131
|
-
|
|
132
|
-
const options = {
|
|
133
|
-
...answers,
|
|
134
|
-
projectDir: path.resolve(process.cwd(), answers.projectName),
|
|
135
|
-
frontendSrcDir: path.resolve(process.cwd(), answers.srcPath),
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
const contracts = await scanFrontend({ frontendSrcDir: options.frontendSrcDir });
|
|
139
|
-
|
|
140
|
-
try {
|
|
141
|
-
console.log(chalk.blue(`\nStarting backend generation for: ${chalk.bold(options.stack)}`));
|
|
142
|
-
await runGeneration(options, contracts);
|
|
143
|
-
|
|
144
|
-
console.log(chalk.green.bold("\nBackend generation complete!"));
|
|
145
|
-
console.log("\nNext Steps:");
|
|
146
|
-
console.log(chalk.cyan(` cd ${options.projectName}`));
|
|
147
|
-
} catch (error) {
|
|
148
|
-
console.error(chalk.red.bold("\nAn error occurred during generation:"));
|
|
149
|
-
console.error(error);
|
|
150
|
-
|
|
151
|
-
if (fs.existsSync(options.projectDir)) {
|
|
152
|
-
console.log(chalk.yellow(" -> Cleaning up failed installation..."));
|
|
153
|
-
fs.removeSync(options.projectDir);
|
|
154
|
-
}
|
|
155
|
-
process.exit(1);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
async function main() {
|
|
160
|
-
const program = new Command();
|
|
161
|
-
|
|
162
|
-
program.name("backlist").description("Backlist CLI").version("5.1.0");
|
|
163
|
-
|
|
164
|
-
program
|
|
165
|
-
.command("scan")
|
|
166
|
-
.description("Scan frontend and write contracts JSON")
|
|
167
|
-
.option("-s, --srcPath <path>", "frontend src path", "src")
|
|
168
|
-
.option("-o, --out <file>", "output contracts file", ".backlist/contracts.json")
|
|
169
|
-
.action(async (flags) => {
|
|
170
|
-
const frontendSrcDir = path.resolve(process.cwd(), flags.srcPath);
|
|
171
|
-
const outFile = path.resolve(process.cwd(), flags.out);
|
|
172
|
-
const contracts = await scanFrontend({ frontendSrcDir });
|
|
173
|
-
await writeContracts(outFile, contracts);
|
|
174
|
-
console.log(chalk.green(`Wrote contracts to ${outFile}`));
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
program
|
|
178
|
-
.command("generate")
|
|
179
|
-
.description("Generate backend using contracts")
|
|
180
|
-
.requiredOption("-k, --stack <stack>", "stack: node-ts-express | dotnet-webapi | java-spring | python-fastapi")
|
|
181
|
-
.option("-p, --projectName <name>", "backend directory", "backend")
|
|
182
|
-
.option("-s, --srcPath <path>", "frontend src path", "src")
|
|
183
|
-
.option("-c, --contracts <file>", "contracts file", ".backlist/contracts.json")
|
|
184
|
-
.action(async (flags) => {
|
|
185
|
-
const options = resolveOptionsFromFlags(flags);
|
|
186
|
-
|
|
187
|
-
const contractsPath = path.resolve(process.cwd(), flags.contracts);
|
|
188
|
-
const contracts = fs.existsSync(contractsPath)
|
|
189
|
-
? await fs.readJson(contractsPath)
|
|
190
|
-
: await scanFrontend({ frontendSrcDir: options.frontendSrcDir });
|
|
191
|
-
|
|
192
|
-
await runGeneration(options, contracts);
|
|
193
|
-
console.log(chalk.green("Generation complete."));
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
program
|
|
197
|
-
.command("watch")
|
|
198
|
-
.description("Watch frontend and regenerate backend on changes")
|
|
199
|
-
.requiredOption("-k, --stack <stack>", "stack")
|
|
200
|
-
.option("-p, --projectName <name>", "backend directory", "backend")
|
|
201
|
-
.option("-s, --srcPath <path>", "frontend src path", "src")
|
|
202
|
-
.action(async (flags) => {
|
|
203
|
-
const options = resolveOptionsFromFlags(flags);
|
|
204
|
-
const watcher = chokidar.watch(options.frontendSrcDir, { ignoreInitial: true });
|
|
205
|
-
|
|
206
|
-
const run = async () => {
|
|
207
|
-
const contracts = await scanFrontend({ frontendSrcDir: options.frontendSrcDir });
|
|
208
|
-
await runGeneration(options, contracts);
|
|
209
|
-
console.log(chalk.green(`[watch] regenerated at ${new Date().toLocaleTimeString()}`));
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
await run();
|
|
213
|
-
watcher.on("add", run).on("change", run).on("unlink", run);
|
|
214
|
-
console.log(chalk.cyan(`[watch] watching ${options.frontendSrcDir}`));
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
if (process.argv.length <= 2) {
|
|
218
|
-
await interactiveMain();
|
|
219
|
-
return;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
await program.parseAsync(process.argv);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
main().catch((e) => {
|
|
226
|
-
console.error(e);
|
|
227
|
-
process.exit(1);
|
|
228
|
-
});
|
package/src/db/prisma.ts
DELETED