tss-stack 1.2.3 → 1.3.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/cli.js +592 -510
- package/package.json +1 -1
- package/src/generators/backend.js +404 -358
- package/src/generators/database.js +169 -55
- package/src/generators/frontend.js +794 -542
- package/src/generators/utils.js +113 -60
package/src/generators/utils.js
CHANGED
|
@@ -1,60 +1,113 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* String and SQL helpers for generator templates.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
const normalizeInput = (value = "") => String(value).trim();
|
|
6
|
-
|
|
7
|
-
const splitSnakeCase = (value = "") =>
|
|
8
|
-
normalizeInput(value)
|
|
9
|
-
.split("_")
|
|
10
|
-
.map((part) => part.trim())
|
|
11
|
-
.filter(Boolean);
|
|
12
|
-
|
|
13
|
-
const capitalize = (value = "") =>
|
|
14
|
-
value ? value.charAt(0).toUpperCase() + value.slice(1) : "";
|
|
15
|
-
|
|
16
|
-
const toPascal = (str = "") => splitSnakeCase(str).map(capitalize).join("");
|
|
17
|
-
|
|
18
|
-
const toCamel = (str = "") => {
|
|
19
|
-
const pascal = toPascal(str);
|
|
20
|
-
return pascal ? pascal.charAt(0).toLowerCase() + pascal.slice(1) : "";
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
const toRoute = (str = "") =>
|
|
24
|
-
normalizeInput(str)
|
|
25
|
-
.replace(/_+/g, "-")
|
|
26
|
-
.replace(/\s+/g, "-")
|
|
27
|
-
.replace(/-+/g, "-")
|
|
28
|
-
.replace(/^-+|-+$/g, "")
|
|
29
|
-
.toLowerCase();
|
|
30
|
-
|
|
31
|
-
const escapeSqlIdentifier = (identifier = "") => {
|
|
32
|
-
const safe = String(identifier).replace(/`/g, "``");
|
|
33
|
-
return `\`${safe}\``;
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
function inferSqlType(field = "") {
|
|
37
|
-
const lower = String(field).toLowerCase().trim();
|
|
38
|
-
|
|
39
|
-
if (!lower) return "VARCHAR(255)";
|
|
40
|
-
if (lower === "id" || lower.endsWith("_id")) return "INT";
|
|
41
|
-
if (lower.includes("quantity") || lower.includes("count") || lower.includes("number")) return "INT";
|
|
42
|
-
if (lower.includes("price") || lower.includes("amount") || lower.includes("total") || lower.includes("cost")) {
|
|
43
|
-
return "DECIMAL(10,2)";
|
|
44
|
-
}
|
|
45
|
-
if (lower.includes("date") || lower.includes("birthday")) return "DATE";
|
|
46
|
-
if (lower.includes("time")) return "DATETIME";
|
|
47
|
-
if (lower.includes("email")) return "VARCHAR(255)";
|
|
48
|
-
if (lower.includes("phone")) return "VARCHAR(30)";
|
|
49
|
-
if (lower.includes("description") || lower.includes("notes") || lower.includes("message")) return "TEXT";
|
|
50
|
-
|
|
51
|
-
return "VARCHAR(255)";
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
1
|
+
/**
|
|
2
|
+
* String and SQL helpers for generator templates.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const normalizeInput = (value = "") => String(value).trim();
|
|
6
|
+
|
|
7
|
+
const splitSnakeCase = (value = "") =>
|
|
8
|
+
normalizeInput(value)
|
|
9
|
+
.split("_")
|
|
10
|
+
.map((part) => part.trim())
|
|
11
|
+
.filter(Boolean);
|
|
12
|
+
|
|
13
|
+
const capitalize = (value = "") =>
|
|
14
|
+
value ? value.charAt(0).toUpperCase() + value.slice(1) : "";
|
|
15
|
+
|
|
16
|
+
const toPascal = (str = "") => splitSnakeCase(str).map(capitalize).join("");
|
|
17
|
+
|
|
18
|
+
const toCamel = (str = "") => {
|
|
19
|
+
const pascal = toPascal(str);
|
|
20
|
+
return pascal ? pascal.charAt(0).toLowerCase() + pascal.slice(1) : "";
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const toRoute = (str = "") =>
|
|
24
|
+
normalizeInput(str)
|
|
25
|
+
.replace(/_+/g, "-")
|
|
26
|
+
.replace(/\s+/g, "-")
|
|
27
|
+
.replace(/-+/g, "-")
|
|
28
|
+
.replace(/^-+|-+$/g, "")
|
|
29
|
+
.toLowerCase();
|
|
30
|
+
|
|
31
|
+
const escapeSqlIdentifier = (identifier = "") => {
|
|
32
|
+
const safe = String(identifier).replace(/`/g, "``");
|
|
33
|
+
return `\`${safe}\``;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
function inferSqlType(field = "") {
|
|
37
|
+
const lower = String(field).toLowerCase().trim();
|
|
38
|
+
|
|
39
|
+
if (!lower) return "VARCHAR(255)";
|
|
40
|
+
if (lower === "id" || lower.endsWith("_id")) return "INT";
|
|
41
|
+
if (lower.includes("quantity") || lower.includes("count") || lower.includes("number")) return "INT";
|
|
42
|
+
if (lower.includes("price") || lower.includes("amount") || lower.includes("total") || lower.includes("cost")) {
|
|
43
|
+
return "DECIMAL(10,2)";
|
|
44
|
+
}
|
|
45
|
+
if (lower.includes("date") || lower.includes("birthday")) return "DATE";
|
|
46
|
+
if (lower.includes("time")) return "DATETIME";
|
|
47
|
+
if (lower.includes("email")) return "VARCHAR(255)";
|
|
48
|
+
if (lower.includes("phone")) return "VARCHAR(30)";
|
|
49
|
+
if (lower.includes("description") || lower.includes("notes") || lower.includes("message")) return "TEXT";
|
|
50
|
+
|
|
51
|
+
return "VARCHAR(255)";
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Analyses a table's fields and categorises them for report generation.
|
|
56
|
+
*
|
|
57
|
+
* metrics — numeric fields worth summing (price, total, quantity, amount, cost)
|
|
58
|
+
* dimensions — categorical fields worth grouping by (status, type, category)
|
|
59
|
+
* dateFields — date/time fields useful for trend queries
|
|
60
|
+
*
|
|
61
|
+
* @param {{ name: string, fields: string[] }} table
|
|
62
|
+
* @returns {{ metrics: string[], dimensions: string[], dateFields: string[] }}
|
|
63
|
+
*/
|
|
64
|
+
function inferReportConfig(table) {
|
|
65
|
+
const metrics = [];
|
|
66
|
+
const dimensions = [];
|
|
67
|
+
const dateFields = [];
|
|
68
|
+
|
|
69
|
+
for (const field of table.fields) {
|
|
70
|
+
const lower = field.toLowerCase();
|
|
71
|
+
|
|
72
|
+
if (
|
|
73
|
+
lower.includes("amount") ||
|
|
74
|
+
lower.includes("price") ||
|
|
75
|
+
lower.includes("total") ||
|
|
76
|
+
lower.includes("quantity") ||
|
|
77
|
+
lower.includes("cost") ||
|
|
78
|
+
lower.includes("count")
|
|
79
|
+
) {
|
|
80
|
+
metrics.push(field);
|
|
81
|
+
} else if (
|
|
82
|
+
lower.includes("date") ||
|
|
83
|
+
lower.includes("created") ||
|
|
84
|
+
lower.includes("updated") ||
|
|
85
|
+
lower.includes("time")
|
|
86
|
+
) {
|
|
87
|
+
dateFields.push(field);
|
|
88
|
+
} else if (
|
|
89
|
+
lower.includes("status") ||
|
|
90
|
+
lower.includes("type") ||
|
|
91
|
+
lower.includes("category") ||
|
|
92
|
+
lower.includes("kind")
|
|
93
|
+
) {
|
|
94
|
+
dimensions.push(field);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Always include created_at as a date dimension — it is added to every table
|
|
99
|
+
if (!dateFields.includes("created_at")) {
|
|
100
|
+
dateFields.push("created_at");
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return { metrics, dimensions, dateFields };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
module.exports = {
|
|
107
|
+
toPascal,
|
|
108
|
+
toCamel,
|
|
109
|
+
toRoute,
|
|
110
|
+
escapeSqlIdentifier,
|
|
111
|
+
inferSqlType,
|
|
112
|
+
inferReportConfig,
|
|
113
|
+
};
|