@currentjs/gen 0.3.2 → 0.5.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/CHANGELOG.md +18 -609
- package/README.md +623 -427
- package/dist/cli.js +2 -1
- package/dist/commands/commit.js +25 -42
- package/dist/commands/createApp.js +1 -0
- package/dist/commands/createModule.js +151 -45
- package/dist/commands/diff.js +27 -40
- package/dist/commands/generateAll.js +141 -291
- package/dist/commands/migrateCommit.js +6 -18
- package/dist/generators/controllerGenerator.d.ts +50 -19
- package/dist/generators/controllerGenerator.js +588 -331
- package/dist/generators/domainLayerGenerator.d.ts +21 -0
- package/dist/generators/domainLayerGenerator.js +286 -0
- package/dist/generators/dtoGenerator.d.ts +21 -0
- package/dist/generators/dtoGenerator.js +523 -0
- package/dist/generators/serviceGenerator.d.ts +22 -51
- package/dist/generators/serviceGenerator.js +345 -568
- package/dist/generators/storeGenerator.d.ts +39 -32
- package/dist/generators/storeGenerator.js +396 -236
- package/dist/generators/templateGenerator.d.ts +21 -21
- package/dist/generators/templateGenerator.js +393 -268
- package/dist/generators/templates/appTemplates.d.ts +3 -1
- package/dist/generators/templates/appTemplates.js +16 -11
- package/dist/generators/templates/data/appYamlTemplate +5 -2
- package/dist/generators/templates/data/cursorRulesTemplate +315 -221
- package/dist/generators/templates/data/frontendScriptTemplate +56 -15
- package/dist/generators/templates/data/mainViewTemplate +2 -1
- package/dist/generators/templates/data/systemTsTemplate +5 -0
- package/dist/generators/templates/index.d.ts +0 -3
- package/dist/generators/templates/index.js +0 -3
- package/dist/generators/templates/storeTemplates.d.ts +1 -5
- package/dist/generators/templates/storeTemplates.js +84 -224
- package/dist/generators/useCaseGenerator.d.ts +13 -0
- package/dist/generators/useCaseGenerator.js +191 -0
- package/dist/types/configTypes.d.ts +149 -0
- package/dist/types/configTypes.js +10 -0
- package/dist/utils/childEntityUtils.d.ts +18 -0
- package/dist/utils/childEntityUtils.js +78 -0
- package/dist/utils/commandUtils.d.ts +43 -0
- package/dist/utils/commandUtils.js +124 -0
- package/dist/utils/commitUtils.d.ts +4 -1
- package/dist/utils/constants.d.ts +10 -0
- package/dist/utils/constants.js +13 -1
- package/dist/utils/diResolver.d.ts +32 -0
- package/dist/utils/diResolver.js +204 -0
- package/dist/utils/typeUtils.d.ts +23 -0
- package/dist/utils/typeUtils.js +77 -0
- package/package.json +7 -3
- package/dist/generators/domainModelGenerator.d.ts +0 -41
- package/dist/generators/domainModelGenerator.js +0 -242
- package/dist/generators/templates/controllerTemplates.d.ts +0 -43
- package/dist/generators/templates/controllerTemplates.js +0 -82
- package/dist/generators/templates/serviceTemplates.d.ts +0 -16
- package/dist/generators/templates/serviceTemplates.js +0 -59
- package/dist/generators/templates/validationTemplates.d.ts +0 -25
- package/dist/generators/templates/validationTemplates.js +0 -66
- package/dist/generators/templates/viewTemplates.d.ts +0 -25
- package/dist/generators/templates/viewTemplates.js +0 -491
- package/dist/generators/validationGenerator.d.ts +0 -29
- package/dist/generators/validationGenerator.js +0 -250
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export interface ConstructorParam {
|
|
2
|
+
name: string;
|
|
3
|
+
type: string;
|
|
4
|
+
}
|
|
5
|
+
export interface ClassInfo {
|
|
6
|
+
className: string;
|
|
7
|
+
filePath: string;
|
|
8
|
+
isController: boolean;
|
|
9
|
+
isInjectable: boolean;
|
|
10
|
+
constructorParams: ConstructorParam[];
|
|
11
|
+
}
|
|
12
|
+
export interface InstantiationStep {
|
|
13
|
+
className: string;
|
|
14
|
+
varName: string;
|
|
15
|
+
importPath: string;
|
|
16
|
+
constructorArgs: string[];
|
|
17
|
+
isController: boolean;
|
|
18
|
+
}
|
|
19
|
+
export declare function parseClassFile(filePath: string): ClassInfo | null;
|
|
20
|
+
export declare function scanModuleClasses(moduleDir: string): ClassInfo[];
|
|
21
|
+
/**
|
|
22
|
+
* @param classes - All discovered @Injectable and @Controller classes
|
|
23
|
+
* @param providerVarByType - Default mapping from interface type to variable name (e.g., ISqlProvider -> dbMysql)
|
|
24
|
+
* @param classProviderVar - Per-class override for provider variable (className -> varName).
|
|
25
|
+
* Used when different modules use different database providers.
|
|
26
|
+
* @param srcDir - Absolute path to src/ directory (for computing relative import paths)
|
|
27
|
+
*/
|
|
28
|
+
export declare function buildInstantiationOrder(classes: ClassInfo[], providerVarByType: Map<string, string>, classProviderVar: Map<string, string>, srcDir: string): InstantiationStep[];
|
|
29
|
+
export declare function resolveProviderImport(importSpec: string, appTsDir: string): {
|
|
30
|
+
importPath: string;
|
|
31
|
+
className: string;
|
|
32
|
+
};
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.parseClassFile = parseClassFile;
|
|
37
|
+
exports.scanModuleClasses = scanModuleClasses;
|
|
38
|
+
exports.buildInstantiationOrder = buildInstantiationOrder;
|
|
39
|
+
exports.resolveProviderImport = resolveProviderImport;
|
|
40
|
+
const fs = __importStar(require("fs"));
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
const INJECTABLE_RE = /@Injectable\s*\(/;
|
|
43
|
+
const CONTROLLER_RE = /@Controller\s*\(/;
|
|
44
|
+
const EXPORT_CLASS_RE = /export\s+class\s+(\w+)/;
|
|
45
|
+
const CONSTRUCTOR_PARAMS_RE = /constructor\s*\(([^)]*)\)/s;
|
|
46
|
+
const PARAM_RE = /(?:private|protected|public)\s+(\w+)\s*:\s*([A-Za-z_]\w*)/g;
|
|
47
|
+
function parseClassFile(filePath) {
|
|
48
|
+
let content;
|
|
49
|
+
try {
|
|
50
|
+
content = fs.readFileSync(filePath, 'utf8');
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
const classMatch = content.match(EXPORT_CLASS_RE);
|
|
56
|
+
if (!classMatch)
|
|
57
|
+
return null;
|
|
58
|
+
const isInjectable = INJECTABLE_RE.test(content);
|
|
59
|
+
const isController = CONTROLLER_RE.test(content);
|
|
60
|
+
if (!isInjectable && !isController)
|
|
61
|
+
return null;
|
|
62
|
+
const className = classMatch[1];
|
|
63
|
+
const constructorParams = [];
|
|
64
|
+
const ctorMatch = content.match(CONSTRUCTOR_PARAMS_RE);
|
|
65
|
+
if (ctorMatch) {
|
|
66
|
+
const paramsSrc = ctorMatch[1].replace(/\n/g, ' ');
|
|
67
|
+
let match;
|
|
68
|
+
const paramRe = new RegExp(PARAM_RE.source, PARAM_RE.flags);
|
|
69
|
+
while ((match = paramRe.exec(paramsSrc))) {
|
|
70
|
+
constructorParams.push({ name: match[1], type: match[2] });
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return { className, filePath, isController, isInjectable, constructorParams };
|
|
74
|
+
}
|
|
75
|
+
function walkTsFiles(dir) {
|
|
76
|
+
const results = [];
|
|
77
|
+
let entries;
|
|
78
|
+
try {
|
|
79
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return results;
|
|
83
|
+
}
|
|
84
|
+
for (const entry of entries) {
|
|
85
|
+
const fullPath = path.join(dir, entry.name);
|
|
86
|
+
if (entry.isDirectory()) {
|
|
87
|
+
results.push(...walkTsFiles(fullPath));
|
|
88
|
+
}
|
|
89
|
+
else if (entry.isFile() && entry.name.endsWith('.ts')) {
|
|
90
|
+
results.push(fullPath);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return results;
|
|
94
|
+
}
|
|
95
|
+
function scanModuleClasses(moduleDir) {
|
|
96
|
+
const tsFiles = walkTsFiles(moduleDir);
|
|
97
|
+
const classes = [];
|
|
98
|
+
for (const file of tsFiles) {
|
|
99
|
+
const info = parseClassFile(file);
|
|
100
|
+
if (info)
|
|
101
|
+
classes.push(info);
|
|
102
|
+
}
|
|
103
|
+
return classes;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* @param classes - All discovered @Injectable and @Controller classes
|
|
107
|
+
* @param providerVarByType - Default mapping from interface type to variable name (e.g., ISqlProvider -> dbMysql)
|
|
108
|
+
* @param classProviderVar - Per-class override for provider variable (className -> varName).
|
|
109
|
+
* Used when different modules use different database providers.
|
|
110
|
+
* @param srcDir - Absolute path to src/ directory (for computing relative import paths)
|
|
111
|
+
*/
|
|
112
|
+
function buildInstantiationOrder(classes, providerVarByType, classProviderVar, srcDir) {
|
|
113
|
+
const classMap = new Map();
|
|
114
|
+
for (const cls of classes) {
|
|
115
|
+
if (!classMap.has(cls.className)) {
|
|
116
|
+
classMap.set(cls.className, cls);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const graph = new Map();
|
|
120
|
+
for (const cls of classMap.values()) {
|
|
121
|
+
const deps = [];
|
|
122
|
+
for (const param of cls.constructorParams) {
|
|
123
|
+
if (classMap.has(param.type)) {
|
|
124
|
+
deps.push(param.type);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
graph.set(cls.className, deps);
|
|
128
|
+
}
|
|
129
|
+
const sorted = topologicalSort(graph);
|
|
130
|
+
const steps = [];
|
|
131
|
+
const varNames = new Map();
|
|
132
|
+
for (const className of sorted) {
|
|
133
|
+
const cls = classMap.get(className);
|
|
134
|
+
const varName = className.charAt(0).toLowerCase() + className.slice(1);
|
|
135
|
+
varNames.set(className, varName);
|
|
136
|
+
const constructorArgs = [];
|
|
137
|
+
for (const param of cls.constructorParams) {
|
|
138
|
+
if (classProviderVar.has(className) && providerVarByType.has(param.type)) {
|
|
139
|
+
constructorArgs.push(classProviderVar.get(className));
|
|
140
|
+
}
|
|
141
|
+
else if (providerVarByType.has(param.type)) {
|
|
142
|
+
constructorArgs.push(providerVarByType.get(param.type));
|
|
143
|
+
}
|
|
144
|
+
else if (varNames.has(param.type)) {
|
|
145
|
+
constructorArgs.push(varNames.get(param.type));
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const relPath = path.relative(srcDir, cls.filePath)
|
|
149
|
+
.replace(/\\/g, '/')
|
|
150
|
+
.replace(/\.ts$/, '');
|
|
151
|
+
const importPath = relPath.startsWith('.') ? relPath : `./${relPath}`;
|
|
152
|
+
steps.push({
|
|
153
|
+
className,
|
|
154
|
+
varName,
|
|
155
|
+
importPath,
|
|
156
|
+
constructorArgs,
|
|
157
|
+
isController: cls.isController
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
return steps;
|
|
161
|
+
}
|
|
162
|
+
function topologicalSort(graph) {
|
|
163
|
+
const visited = new Set();
|
|
164
|
+
const visiting = new Set();
|
|
165
|
+
const result = [];
|
|
166
|
+
function visit(node) {
|
|
167
|
+
if (visited.has(node))
|
|
168
|
+
return;
|
|
169
|
+
if (visiting.has(node)) {
|
|
170
|
+
throw new Error(`Circular dependency detected involving: ${node}`);
|
|
171
|
+
}
|
|
172
|
+
visiting.add(node);
|
|
173
|
+
const deps = graph.get(node) || [];
|
|
174
|
+
for (const dep of deps) {
|
|
175
|
+
if (graph.has(dep)) {
|
|
176
|
+
visit(dep);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
visiting.delete(node);
|
|
180
|
+
visited.add(node);
|
|
181
|
+
result.push(node);
|
|
182
|
+
}
|
|
183
|
+
for (const node of graph.keys()) {
|
|
184
|
+
visit(node);
|
|
185
|
+
}
|
|
186
|
+
return result;
|
|
187
|
+
}
|
|
188
|
+
function resolveProviderImport(importSpec, appTsDir) {
|
|
189
|
+
const baseName = importSpec.split('/').pop() || 'provider';
|
|
190
|
+
const className = baseName
|
|
191
|
+
.replace(/^[^a-zA-Z_]*/g, '')
|
|
192
|
+
.split(/[-_]/)
|
|
193
|
+
.map(s => s.charAt(0).toUpperCase() + s.slice(1))
|
|
194
|
+
.join('');
|
|
195
|
+
const isLocal = importSpec.startsWith('.') || importSpec.startsWith('/');
|
|
196
|
+
if (isLocal) {
|
|
197
|
+
const absPath = path.resolve(appTsDir, importSpec);
|
|
198
|
+
let relPath = path.relative(appTsDir, absPath).replace(/\\/g, '/');
|
|
199
|
+
if (!relPath.startsWith('.'))
|
|
200
|
+
relPath = `./${relPath}`;
|
|
201
|
+
return { importPath: relPath, className };
|
|
202
|
+
}
|
|
203
|
+
return { importPath: importSpec, className };
|
|
204
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared type mapping and string utilities used across generators.
|
|
3
|
+
*/
|
|
4
|
+
export interface TypeMapping {
|
|
5
|
+
[key: string]: string;
|
|
6
|
+
}
|
|
7
|
+
/** Standard YAML type to TypeScript (domain) type mapping. */
|
|
8
|
+
export declare const TYPE_MAPPING: Record<string, string>;
|
|
9
|
+
/** Store-specific: YAML type to DB row TypeScript type (e.g. datetime -> string). */
|
|
10
|
+
export declare const ROW_TYPE_MAPPING: Record<string, string>;
|
|
11
|
+
export declare function capitalize(str: string): string;
|
|
12
|
+
/**
|
|
13
|
+
* Map a YAML field type to TypeScript type, resolving aggregates and value objects by name.
|
|
14
|
+
*/
|
|
15
|
+
export declare function mapType(yamlType: string, aggregates?: Set<string> | Map<string, unknown>, valueObjects?: Set<string> | Map<string, unknown>): string;
|
|
16
|
+
/**
|
|
17
|
+
* Check if a YAML field type references another aggregate entity.
|
|
18
|
+
*/
|
|
19
|
+
export declare function isAggregateReference(yamlType: string, aggregates?: Set<string> | Map<string, unknown>): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Map a YAML type to the store row TypeScript type (value objects become string).
|
|
22
|
+
*/
|
|
23
|
+
export declare function mapRowType(yamlType: string, valueObjects?: Set<string> | Map<string, unknown>): string;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Shared type mapping and string utilities used across generators.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ROW_TYPE_MAPPING = exports.TYPE_MAPPING = void 0;
|
|
7
|
+
exports.capitalize = capitalize;
|
|
8
|
+
exports.mapType = mapType;
|
|
9
|
+
exports.isAggregateReference = isAggregateReference;
|
|
10
|
+
exports.mapRowType = mapRowType;
|
|
11
|
+
/** Standard YAML type to TypeScript (domain) type mapping. */
|
|
12
|
+
exports.TYPE_MAPPING = {
|
|
13
|
+
string: 'string',
|
|
14
|
+
number: 'number',
|
|
15
|
+
integer: 'number',
|
|
16
|
+
decimal: 'number',
|
|
17
|
+
boolean: 'boolean',
|
|
18
|
+
datetime: 'Date',
|
|
19
|
+
date: 'Date',
|
|
20
|
+
id: 'number',
|
|
21
|
+
json: 'any',
|
|
22
|
+
array: 'any[]',
|
|
23
|
+
object: 'object',
|
|
24
|
+
enum: 'string'
|
|
25
|
+
};
|
|
26
|
+
/** Store-specific: YAML type to DB row TypeScript type (e.g. datetime -> string). */
|
|
27
|
+
exports.ROW_TYPE_MAPPING = {
|
|
28
|
+
string: 'string',
|
|
29
|
+
number: 'number',
|
|
30
|
+
integer: 'number',
|
|
31
|
+
decimal: 'number',
|
|
32
|
+
boolean: 'boolean',
|
|
33
|
+
datetime: 'string',
|
|
34
|
+
date: 'string',
|
|
35
|
+
id: 'number',
|
|
36
|
+
json: 'string',
|
|
37
|
+
array: 'string',
|
|
38
|
+
object: 'string',
|
|
39
|
+
enum: 'string'
|
|
40
|
+
};
|
|
41
|
+
function capitalize(str) {
|
|
42
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Map a YAML field type to TypeScript type, resolving aggregates and value objects by name.
|
|
46
|
+
*/
|
|
47
|
+
function mapType(yamlType, aggregates, valueObjects) {
|
|
48
|
+
var _a;
|
|
49
|
+
if (aggregates === null || aggregates === void 0 ? void 0 : aggregates.has(yamlType))
|
|
50
|
+
return yamlType;
|
|
51
|
+
const capitalizedType = capitalize(yamlType);
|
|
52
|
+
if (valueObjects) {
|
|
53
|
+
const has = valueObjects instanceof Set ? valueObjects.has(capitalizedType) : valueObjects.has(capitalizedType);
|
|
54
|
+
if (has)
|
|
55
|
+
return capitalizedType;
|
|
56
|
+
}
|
|
57
|
+
return (_a = exports.TYPE_MAPPING[yamlType]) !== null && _a !== void 0 ? _a : 'any';
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Check if a YAML field type references another aggregate entity.
|
|
61
|
+
*/
|
|
62
|
+
function isAggregateReference(yamlType, aggregates) {
|
|
63
|
+
return !!(aggregates === null || aggregates === void 0 ? void 0 : aggregates.has(yamlType));
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Map a YAML type to the store row TypeScript type (value objects become string).
|
|
67
|
+
*/
|
|
68
|
+
function mapRowType(yamlType, valueObjects) {
|
|
69
|
+
var _a;
|
|
70
|
+
if (valueObjects) {
|
|
71
|
+
const capitalizedType = capitalize(yamlType);
|
|
72
|
+
const has = valueObjects instanceof Set ? valueObjects.has(capitalizedType) : valueObjects.has(capitalizedType);
|
|
73
|
+
if (has)
|
|
74
|
+
return 'string';
|
|
75
|
+
}
|
|
76
|
+
return (_a = exports.ROW_TYPE_MAPPING[yamlType]) !== null && _a !== void 0 ? _a : 'string';
|
|
77
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@currentjs/gen",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "CLI code generator",
|
|
5
5
|
"license": "LGPL-3.0",
|
|
6
6
|
"author": "Konstantin Zavalny",
|
|
@@ -29,8 +29,12 @@
|
|
|
29
29
|
"scripts": {
|
|
30
30
|
"build": "tsc -p tsconfig.json && npm run copy-templates",
|
|
31
31
|
"copy-templates": "mkdir -p dist/generators/templates/data && cp src/generators/templates/data/* dist/generators/templates/data/",
|
|
32
|
-
"clean": "rm -rf dist",
|
|
33
|
-
"prepack": "npm run build"
|
|
32
|
+
"clean": "rm -rf dist dist-test",
|
|
33
|
+
"prepack": "npm run build",
|
|
34
|
+
"test:build": "tsc -p tsconfig.test.json && mkdir -p dist-test/src/generators/templates/data && cp src/generators/templates/data/* dist-test/src/generators/templates/data/",
|
|
35
|
+
"test": "npm run test:build && node --test dist-test/tests/unit/*.test.js",
|
|
36
|
+
"test:integration": "npm run test:build && node --test dist-test/tests/integration/*.test.js",
|
|
37
|
+
"test:all": "npm run test:build && node --test dist-test/tests/unit/*.test.js dist-test/tests/integration/*.test.js"
|
|
34
38
|
},
|
|
35
39
|
"engines": {
|
|
36
40
|
"node": ">=18"
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
interface FieldConfig {
|
|
2
|
-
name: string;
|
|
3
|
-
type: string;
|
|
4
|
-
required?: boolean;
|
|
5
|
-
unique?: boolean;
|
|
6
|
-
auto?: boolean;
|
|
7
|
-
displayFields?: string[];
|
|
8
|
-
}
|
|
9
|
-
interface ModelConfig {
|
|
10
|
-
name: string;
|
|
11
|
-
fields: FieldConfig[];
|
|
12
|
-
}
|
|
13
|
-
type ModuleConfig = {
|
|
14
|
-
models: ModelConfig[];
|
|
15
|
-
};
|
|
16
|
-
type AppConfig = {
|
|
17
|
-
modules: Record<string, ModuleConfig>;
|
|
18
|
-
} | ModuleConfig;
|
|
19
|
-
export declare class DomainModelGenerator {
|
|
20
|
-
private typeMapping;
|
|
21
|
-
private availableModels;
|
|
22
|
-
private getDefaultValue;
|
|
23
|
-
private mapType;
|
|
24
|
-
private setAvailableModels;
|
|
25
|
-
private getRelatedModelImports;
|
|
26
|
-
private generateConstructorParameter;
|
|
27
|
-
private isRelationshipField;
|
|
28
|
-
private getForeignKeyFieldName;
|
|
29
|
-
private generateForeignKeyParameter;
|
|
30
|
-
private generateSetterMethods;
|
|
31
|
-
private sortFieldsByRequired;
|
|
32
|
-
generateModel(modelConfig: ModelConfig): string;
|
|
33
|
-
generateModels(models: ModelConfig[]): string;
|
|
34
|
-
generateFromYamlFile(yamlFilePath: string): Record<string, string>;
|
|
35
|
-
generateFromConfig(config: AppConfig): Record<string, string>;
|
|
36
|
-
generateAndSaveFiles(yamlFilePath: string, outputDir: string, opts?: {
|
|
37
|
-
force?: boolean;
|
|
38
|
-
skipOnConflict?: boolean;
|
|
39
|
-
}): Promise<void>;
|
|
40
|
-
}
|
|
41
|
-
export {};
|
|
@@ -1,242 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.DomainModelGenerator = void 0;
|
|
37
|
-
const yaml_1 = require("yaml");
|
|
38
|
-
const fs = __importStar(require("fs"));
|
|
39
|
-
const path = __importStar(require("path"));
|
|
40
|
-
const generationRegistry_1 = require("../utils/generationRegistry");
|
|
41
|
-
const colors_1 = require("../utils/colors");
|
|
42
|
-
class DomainModelGenerator {
|
|
43
|
-
constructor() {
|
|
44
|
-
this.typeMapping = {
|
|
45
|
-
string: 'string',
|
|
46
|
-
number: 'number',
|
|
47
|
-
boolean: 'boolean',
|
|
48
|
-
datetime: 'Date',
|
|
49
|
-
json: 'any',
|
|
50
|
-
array: 'any[]',
|
|
51
|
-
object: 'object'
|
|
52
|
-
};
|
|
53
|
-
this.availableModels = new Set();
|
|
54
|
-
}
|
|
55
|
-
getDefaultValue(type) {
|
|
56
|
-
switch (type) {
|
|
57
|
-
case 'datetime':
|
|
58
|
-
return 'new Date()';
|
|
59
|
-
case 'string':
|
|
60
|
-
return "''";
|
|
61
|
-
case 'number':
|
|
62
|
-
return '0';
|
|
63
|
-
case 'boolean':
|
|
64
|
-
return 'false';
|
|
65
|
-
case 'array':
|
|
66
|
-
return '[]';
|
|
67
|
-
case 'object':
|
|
68
|
-
case 'json':
|
|
69
|
-
return '{}';
|
|
70
|
-
default:
|
|
71
|
-
return 'undefined';
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
mapType(yamlType) {
|
|
75
|
-
// Check if this is a known model (relationship)
|
|
76
|
-
if (this.availableModels.has(yamlType)) {
|
|
77
|
-
return yamlType;
|
|
78
|
-
}
|
|
79
|
-
return this.typeMapping[yamlType] || 'any';
|
|
80
|
-
}
|
|
81
|
-
setAvailableModels(models) {
|
|
82
|
-
this.availableModels.clear();
|
|
83
|
-
models.forEach(model => {
|
|
84
|
-
this.availableModels.add(model.name);
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
getRelatedModelImports(modelConfig) {
|
|
88
|
-
const imports = [];
|
|
89
|
-
modelConfig.fields.forEach(field => {
|
|
90
|
-
if (this.availableModels.has(field.type) && field.type !== modelConfig.name) {
|
|
91
|
-
imports.push(`import { ${field.type} } from './${field.type}';`);
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
|
-
return imports;
|
|
95
|
-
}
|
|
96
|
-
generateConstructorParameter(field) {
|
|
97
|
-
const tsType = this.mapType(field.type);
|
|
98
|
-
const isOptional = !field.required && !field.auto;
|
|
99
|
-
const hasDefault = field.auto;
|
|
100
|
-
let param = `public ${field.name}`;
|
|
101
|
-
if (isOptional && !hasDefault) {
|
|
102
|
-
param += '?';
|
|
103
|
-
}
|
|
104
|
-
param += `: ${tsType}`;
|
|
105
|
-
if (hasDefault) {
|
|
106
|
-
param += ` = ${this.getDefaultValue(field.type)}`;
|
|
107
|
-
}
|
|
108
|
-
return param;
|
|
109
|
-
}
|
|
110
|
-
isRelationshipField(field) {
|
|
111
|
-
return this.availableModels.has(field.type);
|
|
112
|
-
}
|
|
113
|
-
getForeignKeyFieldName(field) {
|
|
114
|
-
// Convention: fieldName + 'Id' (e.g., owner -> ownerId)
|
|
115
|
-
return field.name + 'Id';
|
|
116
|
-
}
|
|
117
|
-
generateForeignKeyParameter(field) {
|
|
118
|
-
const foreignKeyName = this.getForeignKeyFieldName(field);
|
|
119
|
-
const isOptional = !field.required && !field.auto;
|
|
120
|
-
let param = `public ${foreignKeyName}`;
|
|
121
|
-
if (isOptional) {
|
|
122
|
-
param += '?';
|
|
123
|
-
}
|
|
124
|
-
param += ': number';
|
|
125
|
-
return param;
|
|
126
|
-
}
|
|
127
|
-
generateSetterMethods(modelConfig) {
|
|
128
|
-
const setterMethods = [];
|
|
129
|
-
modelConfig.fields.forEach(field => {
|
|
130
|
-
if (!field.auto && field.name !== 'id') {
|
|
131
|
-
const tsType = this.mapType(field.type);
|
|
132
|
-
const methodName = `set${field.name.charAt(0).toUpperCase() + field.name.slice(1)}`;
|
|
133
|
-
// For all fields (including relationships), generate simple setter
|
|
134
|
-
// Domain model doesn't care about FKs - that's infrastructure concern
|
|
135
|
-
const isOptional = !field.required && !field.auto;
|
|
136
|
-
const setter = `
|
|
137
|
-
${methodName}(${field.name}: ${tsType}${isOptional ? ' | undefined' : ''}): void {
|
|
138
|
-
this.${field.name} = ${field.name};
|
|
139
|
-
}`;
|
|
140
|
-
setterMethods.push(setter);
|
|
141
|
-
}
|
|
142
|
-
});
|
|
143
|
-
return setterMethods.join('\n');
|
|
144
|
-
}
|
|
145
|
-
sortFieldsByRequired(fields) {
|
|
146
|
-
// Sort fields: required fields first, then optional fields
|
|
147
|
-
return [...fields].sort((a, b) => {
|
|
148
|
-
const aRequired = a.required !== false && !a.auto;
|
|
149
|
-
const bRequired = b.required !== false && !b.auto;
|
|
150
|
-
if (aRequired === bRequired) {
|
|
151
|
-
return 0; // Keep original order if both have same required status
|
|
152
|
-
}
|
|
153
|
-
return aRequired ? -1 : 1; // Required fields come first
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
generateModel(modelConfig) {
|
|
157
|
-
const className = modelConfig.name;
|
|
158
|
-
// Always add id field first
|
|
159
|
-
const constructorParams = ['public id: number'];
|
|
160
|
-
// Sort fields to put required fields before optional ones
|
|
161
|
-
const sortedFields = this.sortFieldsByRequired(modelConfig.fields);
|
|
162
|
-
// Process other fields
|
|
163
|
-
sortedFields.forEach(field => {
|
|
164
|
-
// For relationship fields, only add the relationship object (not FK)
|
|
165
|
-
// Domain model works with objects only - FK is infrastructure concern
|
|
166
|
-
constructorParams.push(this.generateConstructorParameter(field));
|
|
167
|
-
});
|
|
168
|
-
const constructorParamsStr = constructorParams.join(',\n ');
|
|
169
|
-
const setterMethods = this.generateSetterMethods(modelConfig);
|
|
170
|
-
// Generate imports for related models
|
|
171
|
-
const imports = this.getRelatedModelImports(modelConfig);
|
|
172
|
-
const importsStr = imports.length > 0 ? imports.join('\n') + '\n\n' : '';
|
|
173
|
-
return `${importsStr}export class ${className} {
|
|
174
|
-
public constructor(
|
|
175
|
-
${constructorParamsStr}
|
|
176
|
-
) { }
|
|
177
|
-
${setterMethods}
|
|
178
|
-
}`;
|
|
179
|
-
}
|
|
180
|
-
generateModels(models) {
|
|
181
|
-
return models.map(model => this.generateModel(model)).join('\n\n');
|
|
182
|
-
}
|
|
183
|
-
generateFromYamlFile(yamlFilePath) {
|
|
184
|
-
const yamlContent = fs.readFileSync(yamlFilePath, 'utf8');
|
|
185
|
-
const config = (0, yaml_1.parse)(yamlContent);
|
|
186
|
-
const result = {};
|
|
187
|
-
if (config.modules) {
|
|
188
|
-
const app = config;
|
|
189
|
-
Object.values(app.modules).forEach(moduleConfig => {
|
|
190
|
-
if (moduleConfig.models && moduleConfig.models.length > 0) {
|
|
191
|
-
// Set available models for relationship detection
|
|
192
|
-
this.setAvailableModels(moduleConfig.models);
|
|
193
|
-
moduleConfig.models.forEach(m => {
|
|
194
|
-
result[m.name] = this.generateModel(m);
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
else if (config.models) {
|
|
200
|
-
const module = config;
|
|
201
|
-
// Set available models for relationship detection
|
|
202
|
-
this.setAvailableModels(module.models);
|
|
203
|
-
module.models.forEach(m => {
|
|
204
|
-
result[m.name] = this.generateModel(m);
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
return result;
|
|
208
|
-
}
|
|
209
|
-
generateFromConfig(config) {
|
|
210
|
-
const result = {};
|
|
211
|
-
if (config.modules) {
|
|
212
|
-
Object.values(config.modules).forEach(moduleConfig => {
|
|
213
|
-
if (moduleConfig.models && moduleConfig.models.length > 0) {
|
|
214
|
-
// Set available models for relationship detection
|
|
215
|
-
this.setAvailableModels(moduleConfig.models);
|
|
216
|
-
moduleConfig.models.forEach(m => {
|
|
217
|
-
result[m.name] = this.generateModel(m);
|
|
218
|
-
});
|
|
219
|
-
}
|
|
220
|
-
});
|
|
221
|
-
}
|
|
222
|
-
else if (config.models) {
|
|
223
|
-
const module = config;
|
|
224
|
-
// Set available models for relationship detection
|
|
225
|
-
this.setAvailableModels(module.models);
|
|
226
|
-
module.models.forEach(m => {
|
|
227
|
-
result[m.name] = this.generateModel(m);
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
return result;
|
|
231
|
-
}
|
|
232
|
-
async generateAndSaveFiles(yamlFilePath, outputDir, opts) {
|
|
233
|
-
const codeByEntity = this.generateFromYamlFile(yamlFilePath);
|
|
234
|
-
await Promise.all(Object.entries(codeByEntity).map(([entity, code]) => {
|
|
235
|
-
const filePath = path.join(outputDir, `${entity}.ts`);
|
|
236
|
-
return (0, generationRegistry_1.writeGeneratedFile)(filePath, code, { force: !!(opts === null || opts === void 0 ? void 0 : opts.force), skipOnConflict: !!(opts === null || opts === void 0 ? void 0 : opts.skipOnConflict) });
|
|
237
|
-
}));
|
|
238
|
-
// eslint-disable-next-line no-console
|
|
239
|
-
console.log('\n' + colors_1.colors.green('Domain model files generated successfully!') + '\n');
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
exports.DomainModelGenerator = DomainModelGenerator;
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
export declare const controllerTemplates: {
|
|
2
|
-
controllerClass: string;
|
|
3
|
-
controllerMethod: string;
|
|
4
|
-
userExtraction: string;
|
|
5
|
-
methodImplementations: {
|
|
6
|
-
list: string;
|
|
7
|
-
get: string;
|
|
8
|
-
create: string;
|
|
9
|
-
update: string;
|
|
10
|
-
delete: string;
|
|
11
|
-
empty: string;
|
|
12
|
-
};
|
|
13
|
-
responseFormats: {
|
|
14
|
-
list: string;
|
|
15
|
-
get: string;
|
|
16
|
-
create: string;
|
|
17
|
-
update: string;
|
|
18
|
-
delete: string;
|
|
19
|
-
};
|
|
20
|
-
statusCodes: {
|
|
21
|
-
list: {
|
|
22
|
-
success: number;
|
|
23
|
-
error: number;
|
|
24
|
-
};
|
|
25
|
-
get: {
|
|
26
|
-
success: number;
|
|
27
|
-
error: number;
|
|
28
|
-
};
|
|
29
|
-
create: {
|
|
30
|
-
success: number;
|
|
31
|
-
error: number;
|
|
32
|
-
};
|
|
33
|
-
update: {
|
|
34
|
-
success: number;
|
|
35
|
-
error: number;
|
|
36
|
-
};
|
|
37
|
-
delete: {
|
|
38
|
-
success: number;
|
|
39
|
-
error: number;
|
|
40
|
-
};
|
|
41
|
-
};
|
|
42
|
-
};
|
|
43
|
-
export declare const controllerFileTemplate = "import { {{ENTITY_NAME}} } from '../../domain/entities/{{ENTITY_NAME}}';\nimport { {{ENTITY_NAME}}Service } from '../../application/services/{{ENTITY_NAME}}Service';\nimport { {{ENTITY_NAME}}DTO } from '../../application/validation/{{ENTITY_NAME}}Validation';{{JWT_IMPORT}}\nimport { Get, Post, Put, Patch, Delete, Controller, Render } from '@currentjs/router';\nimport type { IContext } from '@currentjs/router';\n\n{{CONTROLLER_CLASS}}";
|