@ifecodes/backend-template 1.1.8 → 1.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.
- package/LICENSE +201 -0
- package/README.md +423 -365
- package/bin/cli.js +1276 -836
- package/bin/lib/microservice-config.js +155 -150
- package/bin/lib/prompts.js +277 -241
- package/bin/lib/readme-generator.js +364 -335
- package/bin/lib/service-setup.js +901 -679
- package/package.json +64 -55
- package/template/base/js/.env.example +5 -5
- package/template/base/js/.eslintrc.json +10 -13
- package/template/base/js/.husky/pre-commit +1 -7
- package/template/base/js/.prettierrc +7 -7
- package/template/base/js/eslint.config.js +33 -31
- package/template/base/js/package.json +29 -28
- package/template/base/js/src/app.js +20 -15
- package/template/base/js/src/config/env.js +32 -2
- package/template/base/js/src/config/index.js +2 -2
- package/template/base/js/src/docs/index.js +5 -0
- package/template/base/js/src/docs/route-registry.js +63 -0
- package/template/base/js/src/middlewares/error-handler.middleware.js +22 -0
- package/template/base/js/src/middlewares/index.js +9 -3
- package/template/base/js/src/middlewares/method-not-allowed.middleware.js +8 -2
- package/template/base/js/src/middlewares/not-found.middleware.js +4 -1
- package/template/base/js/src/middlewares/observability.middleware.js +24 -0
- package/template/base/js/src/middlewares/root.middleware.js +7 -5
- package/template/base/js/src/middlewares/validation.middleware.js +39 -0
- package/template/base/js/src/modules/index.js +8 -8
- package/template/base/js/src/modules/v1/health/health.controller.auth.js +29 -0
- package/template/base/js/src/modules/v1/health/health.controller.js +4 -4
- package/template/base/js/src/modules/v1/health/health.route.js +70 -5
- package/template/base/js/src/modules/v1/health/index.js +1 -1
- package/template/base/js/src/modules/v1/index.js +3 -3
- package/template/base/js/src/routes.js +13 -6
- package/template/base/js/src/server.js +18 -18
- package/template/base/js/src/utils/http-error.js +27 -6
- package/template/base/js/src/utils/index.js +28 -22
- package/template/base/js/src/utils/logger.js +57 -67
- package/template/base/ts/.eslintrc.json +13 -17
- package/template/base/ts/.prettierrc +7 -7
- package/template/base/ts/eslint.config.js +33 -33
- package/template/base/ts/package.json +41 -39
- package/template/base/ts/src/app.ts +20 -15
- package/template/base/ts/src/config/db.ts +4 -4
- package/template/base/ts/src/config/env.ts +40 -10
- package/template/base/ts/src/config/index.ts +2 -2
- package/template/base/ts/src/docs/index.ts +3 -0
- package/template/base/ts/src/docs/route-registry.ts +98 -0
- package/template/base/ts/src/middlewares/error-handler.middleware.ts +26 -0
- package/template/base/ts/src/middlewares/index.ts +6 -3
- package/template/base/ts/src/middlewares/method-not-allowed.middleware.ts +23 -16
- package/template/base/ts/src/middlewares/not-found.middleware.ts +10 -8
- package/template/base/ts/src/middlewares/observability.middleware.ts +25 -0
- package/template/base/ts/src/middlewares/root.middleware.ts +16 -14
- package/template/base/ts/src/middlewares/validation.middleware.ts +46 -0
- package/template/base/ts/src/modules/index.ts +8 -8
- package/template/base/ts/src/modules/v1/health/health.controller.auth.ts +26 -0
- package/template/base/ts/src/modules/v1/health/health.controller.ts +18 -18
- package/template/base/ts/src/modules/v1/health/health.route.ts +68 -9
- package/template/base/ts/src/modules/v1/health/index.ts +1 -1
- package/template/base/ts/src/modules/v1/index.ts +8 -8
- package/template/base/ts/src/routes.ts +23 -15
- package/template/base/ts/src/server.ts +19 -19
- package/template/base/ts/src/utils/http-error.ts +63 -45
- package/template/base/ts/src/utils/index.ts +14 -11
- package/template/base/ts/src/utils/logger.ts +58 -68
- package/template/base/ts/tsconfig.json +21 -22
- package/template/features/auth/argon2/inject.js +50 -50
- package/template/features/auth/base/health-openapi.ts +62 -0
- package/template/features/auth/base/inject.js +174 -172
- package/template/features/auth/bcrypt/inject.js +40 -40
- package/template/features/auth/models/user.model.js +24 -24
- package/template/features/auth/models/user.model.ts +1 -1
- package/template/features/auth/modules/auth.controller.js +21 -21
- package/template/features/auth/modules/auth.controller.ts +28 -20
- package/template/features/auth/modules/auth.routes.js +89 -10
- package/template/features/auth/modules/auth.routes.ts +86 -11
- package/template/features/auth/modules/auth.service.js +29 -29
- package/template/features/auth/modules/auth.service.ts +38 -38
- package/template/features/auth/modules/index.js +1 -1
- package/template/features/auth/utils/hash.ts +20 -20
- package/template/features/auth/utils/jwt.js +12 -12
- package/template/features/cors/inject.js +14 -13
- package/template/features/helmet/inject.js +7 -6
- package/template/features/morgan/inject.js +8 -7
- package/template/features/rate-limit/inject.js +7 -6
- package/template/gateway/js/app.js +42 -42
- package/template/gateway/js/inject.js +33 -31
- package/template/gateway/js/server.js +19 -19
- package/template/gateway/ts/app.ts +43 -43
- package/template/gateway/ts/inject.js +33 -33
- package/template/gateway/ts/server.ts +19 -19
- package/template/microservice/docker/docker-compose.yml +5 -5
- package/template/microservice/nodocker/pm2.config.js +3 -3
package/bin/lib/prompts.js
CHANGED
|
@@ -1,241 +1,277 @@
|
|
|
1
|
-
import prompts from "prompts";
|
|
2
|
-
import pc from "picocolors";
|
|
3
|
-
import fs from "fs";
|
|
4
|
-
import path from "path";
|
|
5
|
-
|
|
6
|
-
export const getProjectConfig = async () => {
|
|
7
|
-
// Check if running in CI or non-interactive mode
|
|
8
|
-
const isCI = process.env.CI ===
|
|
9
|
-
|
|
10
|
-
// Check if we're in an existing microservice project
|
|
11
|
-
const isInMicroserviceProject = fs.existsSync(
|
|
12
|
-
path.join(process.cwd(), "services")
|
|
13
|
-
);
|
|
14
|
-
|
|
15
|
-
// Parse command line arguments
|
|
16
|
-
const args = process.argv.slice(2);
|
|
17
|
-
|
|
18
|
-
// Separate project name parts from project type
|
|
19
|
-
// Look for "microservice", "monolith", "micro", or "mono" in args
|
|
20
|
-
let cliName = null;
|
|
21
|
-
let cliProjectType = null;
|
|
22
|
-
|
|
23
|
-
const projectTypeKeywords = ["microservice", "monolith", "micro", "mono"];
|
|
24
|
-
const projectTypeIndex = args.findIndex(arg =>
|
|
25
|
-
projectTypeKeywords.includes(arg.toLowerCase())
|
|
26
|
-
);
|
|
27
|
-
|
|
28
|
-
if (projectTypeIndex !== -1) {
|
|
29
|
-
// Everything before the type keyword is the project name
|
|
30
|
-
cliName = args.slice(0, projectTypeIndex).join("-");
|
|
31
|
-
const typeArg = args[projectTypeIndex].toLowerCase();
|
|
32
|
-
cliProjectType =
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
:
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
if (
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
mode =
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
//
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
1
|
+
import prompts from "prompts";
|
|
2
|
+
import pc from "picocolors";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import path from "path";
|
|
5
|
+
|
|
6
|
+
export const getProjectConfig = async () => {
|
|
7
|
+
// Check if running in CI or non-interactive mode
|
|
8
|
+
const isCI = process.env.CI === "true" || !process.stdin.isTTY;
|
|
9
|
+
|
|
10
|
+
// Check if we're in an existing microservice project
|
|
11
|
+
const isInMicroserviceProject = fs.existsSync(
|
|
12
|
+
path.join(process.cwd(), "services")
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
// Parse command line arguments
|
|
16
|
+
const args = process.argv.slice(2);
|
|
17
|
+
|
|
18
|
+
// Separate project name parts from project type
|
|
19
|
+
// Look for "microservice", "monolith", "micro", or "mono" in args
|
|
20
|
+
let cliName = null;
|
|
21
|
+
let cliProjectType = null;
|
|
22
|
+
|
|
23
|
+
const projectTypeKeywords = ["microservice", "monolith", "micro", "mono"];
|
|
24
|
+
const projectTypeIndex = args.findIndex((arg) =>
|
|
25
|
+
projectTypeKeywords.includes(arg.toLowerCase())
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
if (projectTypeIndex !== -1) {
|
|
29
|
+
// Everything before the type keyword is the project name
|
|
30
|
+
cliName = args.slice(0, projectTypeIndex).join("-");
|
|
31
|
+
const typeArg = args[projectTypeIndex].toLowerCase();
|
|
32
|
+
cliProjectType =
|
|
33
|
+
typeArg === "micro" || typeArg === "microservice"
|
|
34
|
+
? "microservice"
|
|
35
|
+
: "monolith";
|
|
36
|
+
} else if (args.length > 0) {
|
|
37
|
+
// If no type keyword found, treat all args as project name
|
|
38
|
+
cliName = args.join("-");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Pre-fill values from CLI args if provided
|
|
42
|
+
const hasCliArgs = cliName && !isInMicroserviceProject;
|
|
43
|
+
|
|
44
|
+
const res = await prompts(
|
|
45
|
+
[
|
|
46
|
+
{
|
|
47
|
+
type: isInMicroserviceProject || hasCliArgs || isCI ? null : "text",
|
|
48
|
+
name: "name",
|
|
49
|
+
message: pc.cyan("Project name"),
|
|
50
|
+
initial: "my-backend",
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
type: isInMicroserviceProject || isCI ? null : "select",
|
|
54
|
+
name: "language",
|
|
55
|
+
message: pc.cyan("Select language"),
|
|
56
|
+
choices: [
|
|
57
|
+
{ title: pc.green("TypeScript"), value: "typescript" },
|
|
58
|
+
{ title: pc.yellow("JavaScript"), value: "javascript" },
|
|
59
|
+
],
|
|
60
|
+
initial: 0,
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
type: isInMicroserviceProject || isCI ? null : "text",
|
|
64
|
+
name: "description",
|
|
65
|
+
message: pc.cyan("Project description") + pc.dim(" (optional)"),
|
|
66
|
+
initial: "",
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
type: isInMicroserviceProject || isCI ? null : "text",
|
|
70
|
+
name: "author",
|
|
71
|
+
message: pc.cyan("Author") + pc.dim(" (optional)"),
|
|
72
|
+
initial: "",
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
type: isInMicroserviceProject || isCI ? null : "text",
|
|
76
|
+
name: "keywords",
|
|
77
|
+
message: pc.cyan("Keywords") + pc.dim(" (comma-separated, optional)"),
|
|
78
|
+
initial: "",
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
type:
|
|
82
|
+
isInMicroserviceProject || (hasCliArgs && cliProjectType) || isCI
|
|
83
|
+
? null
|
|
84
|
+
: "select",
|
|
85
|
+
name: "projectType",
|
|
86
|
+
message: pc.cyan("Project type"),
|
|
87
|
+
choices: [
|
|
88
|
+
{ title: pc.blue("Monolith API"), value: "monolith" },
|
|
89
|
+
{ title: pc.magenta("Microservice"), value: "microservice" },
|
|
90
|
+
],
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
type: (prev, values) => {
|
|
94
|
+
// Skip if in existing microservice project or CI mode
|
|
95
|
+
if (isInMicroserviceProject || isCI) return null;
|
|
96
|
+
|
|
97
|
+
// Get projectType from previous answers or CLI args
|
|
98
|
+
const projectType = prev || cliProjectType || values.projectType;
|
|
99
|
+
|
|
100
|
+
// Show prompt only if projectType is microservice
|
|
101
|
+
return projectType === "microservice" ? "select" : null;
|
|
102
|
+
},
|
|
103
|
+
name: "mode",
|
|
104
|
+
message: pc.cyan("Microservice setup"),
|
|
105
|
+
choices: [
|
|
106
|
+
{ title: pc.blue("With Docker 🐳"), value: "docker" },
|
|
107
|
+
{ title: pc.yellow("Without Docker (PM2)"), value: "nodocker" },
|
|
108
|
+
],
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
type: isInMicroserviceProject ? "text" : isCI ? null : "multiselect",
|
|
112
|
+
name: isInMicroserviceProject ? "serviceName" : "features",
|
|
113
|
+
message: isInMicroserviceProject
|
|
114
|
+
? pc.cyan("New service name") +
|
|
115
|
+
pc.dim(" (e.g., user-service, order-service)")
|
|
116
|
+
: pc.cyan("Select features"),
|
|
117
|
+
choices: isInMicroserviceProject
|
|
118
|
+
? undefined
|
|
119
|
+
: [
|
|
120
|
+
{ title: pc.blue("CORS"), value: "cors" },
|
|
121
|
+
{ title: pc.yellow("Rate Limiter"), value: "rate-limit" },
|
|
122
|
+
{ title: pc.green("Helmet"), value: "helmet" },
|
|
123
|
+
{ title: pc.magenta("Morgan (HTTP logger)"), value: "morgan" },
|
|
124
|
+
],
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
type: isCI ? null : "toggle",
|
|
128
|
+
name: "auth",
|
|
129
|
+
message: isInMicroserviceProject
|
|
130
|
+
? pc.cyan("Include authentication in this service?")
|
|
131
|
+
: pc.cyan("Include authentication system?"),
|
|
132
|
+
initial: true,
|
|
133
|
+
active: pc.green("yes"),
|
|
134
|
+
inactive: pc.red("no"),
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
type: isInMicroserviceProject && !isCI ? "multiselect" : null,
|
|
138
|
+
name: "features",
|
|
139
|
+
message: pc.cyan("Select features for this service"),
|
|
140
|
+
choices: [
|
|
141
|
+
{ title: pc.yellow("Rate Limiter"), value: "rate-limit" },
|
|
142
|
+
{ title: pc.green("Helmet"), value: "helmet" },
|
|
143
|
+
{ title: pc.magenta("Morgan (HTTP logger)"), value: "morgan" },
|
|
144
|
+
{ title: pc.blue("CORS"), value: "cors" },
|
|
145
|
+
],
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
type: isInMicroserviceProject || isCI ? null : "select",
|
|
149
|
+
name: "projectScope",
|
|
150
|
+
message: pc.cyan("Is this a team or individual project?"),
|
|
151
|
+
choices: [
|
|
152
|
+
{ title: pc.green("Team project"), value: "team" },
|
|
153
|
+
{ title: pc.yellow("Individual project"), value: "individual" },
|
|
154
|
+
],
|
|
155
|
+
initial: 0,
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
type: isInMicroserviceProject || isCI ? null : "toggle",
|
|
159
|
+
name: "validation",
|
|
160
|
+
message: pc.cyan("Include request validation with Zod?"),
|
|
161
|
+
initial: false,
|
|
162
|
+
active: pc.green("yes"),
|
|
163
|
+
inactive: pc.red("no"),
|
|
164
|
+
},
|
|
165
|
+
],
|
|
166
|
+
{
|
|
167
|
+
onCancel: () => {
|
|
168
|
+
console.log(pc.yellow("\n❌ Operation cancelled by user."));
|
|
169
|
+
process.exit(0);
|
|
170
|
+
},
|
|
171
|
+
}
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
// Handle cancelled prompts (user pressed Ctrl+C or closed the prompt)
|
|
175
|
+
// Check if user cancelled the prompt (Ctrl+C) vs just didn't enter anything
|
|
176
|
+
if (!res) {
|
|
177
|
+
console.log(pc.yellow("\n❌ Operation cancelled by user."));
|
|
178
|
+
process.exit(0);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Check if critical fields are missing (indicates cancellation mid-prompt)
|
|
182
|
+
if (!isInMicroserviceProject && !isCI) {
|
|
183
|
+
// For new projects, we need language and projectType
|
|
184
|
+
if (!res.language || (!res.projectType && !cliProjectType)) {
|
|
185
|
+
console.log(pc.yellow("\n❌ Operation cancelled by user."));
|
|
186
|
+
process.exit(0);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// If the response is empty but we expected prompts, something went wrong
|
|
191
|
+
if (
|
|
192
|
+
Object.keys(res).length === 0 &&
|
|
193
|
+
!isInMicroserviceProject &&
|
|
194
|
+
!hasCliArgs &&
|
|
195
|
+
!isCI
|
|
196
|
+
) {
|
|
197
|
+
console.log(pc.red("\n❌ No inputs received. Please try again."));
|
|
198
|
+
process.exit(1);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Set defaults for CI/non-interactive mode
|
|
202
|
+
if (isCI) {
|
|
203
|
+
res.features = res.features || [];
|
|
204
|
+
res.auth = res.auth ?? false;
|
|
205
|
+
res.mode = res.mode || "docker"; // Default to docker in CI
|
|
206
|
+
res.language = res.language || "typescript"; // Default to TypeScript in CI
|
|
207
|
+
res.projectScope = res.projectScope || "team";
|
|
208
|
+
res.validation = res.validation ?? false;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Merge CLI args with prompted responses
|
|
212
|
+
if (hasCliArgs) {
|
|
213
|
+
res.name = cliName;
|
|
214
|
+
if (cliProjectType) {
|
|
215
|
+
res.projectType = cliProjectType;
|
|
216
|
+
} else {
|
|
217
|
+
// If no project type in CLI, default to monolith
|
|
218
|
+
res.projectType = res.projectType || "monolith";
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Ensure we have a project name (fallback if somehow missed)
|
|
223
|
+
if (!res.name && !isInMicroserviceProject) {
|
|
224
|
+
res.name = "my-backend";
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Derive a CI/CD preference from the project scope so later generators can use it.
|
|
228
|
+
res.cicd = !isInMicroserviceProject && res.projectScope !== "individual";
|
|
229
|
+
|
|
230
|
+
let sanitizedName, target, isExistingProject, mode;
|
|
231
|
+
|
|
232
|
+
if (isInMicroserviceProject) {
|
|
233
|
+
// We're adding to an existing project
|
|
234
|
+
target = process.cwd();
|
|
235
|
+
sanitizedName = path.basename(target);
|
|
236
|
+
isExistingProject = true;
|
|
237
|
+
|
|
238
|
+
// Detect mode from existing files
|
|
239
|
+
mode = fs.existsSync(path.join(target, "docker-compose.yml"))
|
|
240
|
+
? "docker"
|
|
241
|
+
: "nodocker";
|
|
242
|
+
|
|
243
|
+
console.log(
|
|
244
|
+
pc.cyan(`\n📁 Detected existing microservice project: ${sanitizedName}`)
|
|
245
|
+
);
|
|
246
|
+
console.log(pc.dim(`Mode: ${mode}\n`));
|
|
247
|
+
} else {
|
|
248
|
+
sanitizedName = res.name.replace(/\s+/g, "-");
|
|
249
|
+
target = path.resolve(process.cwd(), sanitizedName);
|
|
250
|
+
isExistingProject = fs.existsSync(target);
|
|
251
|
+
mode = res.mode;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// If adding to an existing microservice project, normalize the provided service name
|
|
255
|
+
if (isInMicroserviceProject && res.serviceName) {
|
|
256
|
+
res.serviceName = normalizeServiceName(res.serviceName);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return {
|
|
260
|
+
...res,
|
|
261
|
+
sanitizedName,
|
|
262
|
+
target,
|
|
263
|
+
isExistingProject,
|
|
264
|
+
mode,
|
|
265
|
+
isInMicroserviceProject,
|
|
266
|
+
};
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
// Normalize serviceName for existing microservice projects: ensure it ends with '-service'
|
|
270
|
+
// and is kebab-cased/lowercase for consistency.
|
|
271
|
+
function normalizeServiceName(name) {
|
|
272
|
+
if (!name) return name;
|
|
273
|
+
let svc = String(name).trim().toLowerCase();
|
|
274
|
+
svc = svc.replace(/\s+/g, "-").replace(/[^a-z0-9\-]/g, "");
|
|
275
|
+
if (!svc.endsWith("-service")) svc = `${svc}-service`;
|
|
276
|
+
return svc;
|
|
277
|
+
}
|