create-backlist 6.1.8 → 6.1.9
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/package.json +1 -1
- package/src/generators/node.js +40 -19
package/package.json
CHANGED
package/src/generators/node.js
CHANGED
|
@@ -7,11 +7,26 @@ const ejs = require("ejs");
|
|
|
7
7
|
const { analyzeFrontend } = require("../analyzer");
|
|
8
8
|
const { renderAndWrite, getTemplatePath } = require("./template");
|
|
9
9
|
|
|
10
|
+
function stripQuery(p) {
|
|
11
|
+
return String(p || "").split("?")[0];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function safePascalName(name) {
|
|
15
|
+
// remove query + invalid filename chars, keep alphanumerics only
|
|
16
|
+
const cleaned = String(name || "Default")
|
|
17
|
+
.split("?")[0]
|
|
18
|
+
.replace(/[^a-zA-Z0-9]/g, "");
|
|
19
|
+
|
|
20
|
+
if (!cleaned) return "Default";
|
|
21
|
+
return cleaned.charAt(0).toUpperCase() + cleaned.slice(1);
|
|
22
|
+
}
|
|
23
|
+
|
|
10
24
|
function sanitizeEndpoints(endpoints) {
|
|
11
25
|
if (!Array.isArray(endpoints)) return [];
|
|
12
26
|
|
|
13
27
|
return endpoints.map((ep) => {
|
|
14
|
-
|
|
28
|
+
// IMPORTANT: strip query string so it never leaks into names
|
|
29
|
+
const rawPath = stripQuery(ep.path || ep.route || "/");
|
|
15
30
|
|
|
16
31
|
// remove empty, api, and version segments (v1/v2/v10...)
|
|
17
32
|
const parts = rawPath
|
|
@@ -20,7 +35,7 @@ function sanitizeEndpoints(endpoints) {
|
|
|
20
35
|
.filter((p) => p !== "api" && !/^v\d+$/i.test(p));
|
|
21
36
|
|
|
22
37
|
const resource = parts[0] || "Default";
|
|
23
|
-
const controllerName =
|
|
38
|
+
const controllerName = safePascalName(resource);
|
|
24
39
|
|
|
25
40
|
let functionName = "";
|
|
26
41
|
|
|
@@ -33,13 +48,15 @@ function sanitizeEndpoints(endpoints) {
|
|
|
33
48
|
const singularName = resource.endsWith("s") ? resource.slice(0, -1) : resource;
|
|
34
49
|
const pluralName = resource.endsWith("s") ? resource : `${resource}s`;
|
|
35
50
|
|
|
36
|
-
const pascalSingular =
|
|
37
|
-
const pascalPlural =
|
|
51
|
+
const pascalSingular = safePascalName(singularName);
|
|
52
|
+
const pascalPlural = safePascalName(pluralName);
|
|
38
53
|
|
|
39
54
|
const method = String(ep.method || "GET").toUpperCase();
|
|
40
55
|
|
|
41
|
-
|
|
42
|
-
|
|
56
|
+
const hasId =
|
|
57
|
+
rawPath.includes(":") || // :id style
|
|
58
|
+
rawPath.includes("{") || // {id} style
|
|
59
|
+
/\/\d+/.test(rawPath); // numeric
|
|
43
60
|
|
|
44
61
|
if (method === "GET") {
|
|
45
62
|
functionName = hasId ? `get${pascalSingular}ById` : `getAll${pascalPlural}`;
|
|
@@ -54,7 +71,8 @@ function sanitizeEndpoints(endpoints) {
|
|
|
54
71
|
}
|
|
55
72
|
}
|
|
56
73
|
|
|
57
|
-
return
|
|
74
|
+
// return with clean path (query removed)
|
|
75
|
+
return { ...ep, path: rawPath, controllerName, functionName };
|
|
58
76
|
});
|
|
59
77
|
}
|
|
60
78
|
|
|
@@ -76,10 +94,11 @@ async function generateNodeProject(options) {
|
|
|
76
94
|
console.log(chalk.blue(" -> Analyzing frontend for API endpoints..."));
|
|
77
95
|
let endpoints = await analyzeFrontend(frontendSrcDir);
|
|
78
96
|
|
|
79
|
-
if (endpoints.length > 0) {
|
|
97
|
+
if (Array.isArray(endpoints) && endpoints.length > 0) {
|
|
80
98
|
console.log(chalk.green(` -> Found ${endpoints.length} endpoints.`));
|
|
81
99
|
endpoints = sanitizeEndpoints(endpoints);
|
|
82
100
|
} else {
|
|
101
|
+
endpoints = [];
|
|
83
102
|
console.log(chalk.yellow(" -> No API endpoints found. A basic project will be created."));
|
|
84
103
|
}
|
|
85
104
|
|
|
@@ -87,9 +106,12 @@ async function generateNodeProject(options) {
|
|
|
87
106
|
const modelsToGenerate = new Map();
|
|
88
107
|
|
|
89
108
|
endpoints.forEach((ep) => {
|
|
90
|
-
if (!ep
|
|
109
|
+
if (!ep) return;
|
|
91
110
|
|
|
92
|
-
|
|
111
|
+
const ctrl = safePascalName(ep.controllerName);
|
|
112
|
+
if (ctrl === "Default" || ctrl === "Auth") return;
|
|
113
|
+
|
|
114
|
+
if (!modelsToGenerate.has(ctrl)) {
|
|
93
115
|
let fields = [];
|
|
94
116
|
if (ep.schemaFields) {
|
|
95
117
|
fields = Object.entries(ep.schemaFields).map(([key, type]) => ({
|
|
@@ -98,8 +120,7 @@ async function generateNodeProject(options) {
|
|
|
98
120
|
isUnique: key === "email",
|
|
99
121
|
}));
|
|
100
122
|
}
|
|
101
|
-
|
|
102
|
-
modelsToGenerate.set(ep.controllerName, { name: ep.controllerName, fields });
|
|
123
|
+
modelsToGenerate.set(ctrl, { name: ctrl, fields });
|
|
103
124
|
}
|
|
104
125
|
});
|
|
105
126
|
|
|
@@ -175,7 +196,7 @@ async function generateNodeProject(options) {
|
|
|
175
196
|
await fs.ensureDir(path.join(destSrcDir, "models"));
|
|
176
197
|
|
|
177
198
|
for (const [modelName, modelData] of modelsToGenerate.entries()) {
|
|
178
|
-
const schema = modelData.fields.reduce((acc, field) => {
|
|
199
|
+
const schema = (modelData.fields || []).reduce((acc, field) => {
|
|
179
200
|
acc[field.name] = field.type;
|
|
180
201
|
return acc;
|
|
181
202
|
}, {});
|
|
@@ -245,10 +266,11 @@ async function generateNodeProject(options) {
|
|
|
245
266
|
// --- Step 8: Extras ---
|
|
246
267
|
if (extraFeatures.includes("docker")) {
|
|
247
268
|
console.log(chalk.blue(" -> Generating Docker files..."));
|
|
248
|
-
await renderAndWrite(
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
269
|
+
await renderAndWrite(
|
|
270
|
+
getTemplatePath("node-ts-express/partials/Dockerfile.ejs"),
|
|
271
|
+
path.join(projectDir, "Dockerfile"),
|
|
272
|
+
{ dbType, port }
|
|
273
|
+
);
|
|
252
274
|
await renderAndWrite(
|
|
253
275
|
getTemplatePath("node-ts-express/partials/docker-compose.yml.ejs"),
|
|
254
276
|
path.join(projectDir, "docker-compose.yml"),
|
|
@@ -274,7 +296,6 @@ async function generateNodeProject(options) {
|
|
|
274
296
|
await fs.writeFile(path.join(projectDir, "jest.config.js"), jestConfig);
|
|
275
297
|
await fs.ensureDir(path.join(projectDir, "src", "__tests__"));
|
|
276
298
|
|
|
277
|
-
// IMPORTANT: pass endpoints for auto-tests
|
|
278
299
|
await renderAndWrite(
|
|
279
300
|
getTemplatePath("node-ts-express/partials/App.test.ts.ejs"),
|
|
280
301
|
path.join(projectDir, "src", "__tests__", "api.test.ts"),
|
|
@@ -283,7 +304,7 @@ async function generateNodeProject(options) {
|
|
|
283
304
|
}
|
|
284
305
|
|
|
285
306
|
// --- Step 9: routes.ts + server inject ---
|
|
286
|
-
const nonAuthEndpoints = endpoints.filter((ep) => ep.controllerName !== "Auth");
|
|
307
|
+
const nonAuthEndpoints = endpoints.filter((ep) => safePascalName(ep.controllerName) !== "Auth");
|
|
287
308
|
|
|
288
309
|
await renderAndWrite(
|
|
289
310
|
getTemplatePath("node-ts-express/partials/routes.ts.ejs"),
|