@currentjs/gen 0.3.1 → 0.5.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/CHANGELOG.md +8 -289
- 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/commands/migratePush.d.ts +1 -0
- package/dist/commands/migratePush.js +135 -0
- package/dist/commands/migrateUpdate.d.ts +1 -0
- package/dist/commands/migrateUpdate.js +147 -0
- package/dist/commands/newGenerateAll.d.ts +4 -0
- package/dist/commands/newGenerateAll.js +336 -0
- package/dist/generators/controllerGenerator.d.ts +43 -19
- package/dist/generators/controllerGenerator.js +547 -329
- package/dist/generators/domainLayerGenerator.d.ts +21 -0
- package/dist/generators/domainLayerGenerator.js +276 -0
- package/dist/generators/dtoGenerator.d.ts +21 -0
- package/dist/generators/dtoGenerator.js +518 -0
- package/dist/generators/newControllerGenerator.d.ts +55 -0
- package/dist/generators/newControllerGenerator.js +644 -0
- package/dist/generators/newServiceGenerator.d.ts +19 -0
- package/dist/generators/newServiceGenerator.js +266 -0
- package/dist/generators/newStoreGenerator.d.ts +39 -0
- package/dist/generators/newStoreGenerator.js +408 -0
- package/dist/generators/newTemplateGenerator.d.ts +29 -0
- package/dist/generators/newTemplateGenerator.js +510 -0
- package/dist/generators/serviceGenerator.d.ts +16 -51
- package/dist/generators/serviceGenerator.js +167 -586
- package/dist/generators/storeGenerator.d.ts +35 -32
- package/dist/generators/storeGenerator.js +291 -238
- package/dist/generators/storeGeneratorV2.d.ts +31 -0
- package/dist/generators/storeGeneratorV2.js +190 -0
- 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 +15 -10
- package/dist/generators/templates/data/appYamlTemplate +5 -2
- package/dist/generators/templates/data/cursorRulesTemplate +315 -221
- package/dist/generators/templates/data/frontendScriptTemplate +76 -47
- package/dist/generators/templates/data/mainViewTemplate +1 -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/newStoreTemplates.d.ts +5 -0
- package/dist/generators/templates/newStoreTemplates.js +141 -0
- package/dist/generators/templates/storeTemplates.d.ts +1 -5
- package/dist/generators/templates/storeTemplates.js +102 -219
- package/dist/generators/templates/viewTemplates.js +1 -1
- package/dist/generators/useCaseGenerator.d.ts +13 -0
- package/dist/generators/useCaseGenerator.js +188 -0
- package/dist/types/configTypes.d.ts +148 -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/new_parts_of_migrationUtils.d.ts +0 -0
- package/dist/utils/new_parts_of_migrationUtils.js +164 -0
- package/dist/utils/typeUtils.d.ts +19 -0
- package/dist/utils/typeUtils.js +70 -0
- package/package.json +7 -3
|
@@ -0,0 +1,266 @@
|
|
|
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.NewServiceGenerator = 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
|
+
const configTypes_1 = require("../types/configTypes");
|
|
43
|
+
const childEntityUtils_1 = require("../utils/childEntityUtils");
|
|
44
|
+
class NewServiceGenerator {
|
|
45
|
+
constructor() {
|
|
46
|
+
this.typeMapping = {
|
|
47
|
+
string: 'string',
|
|
48
|
+
number: 'number',
|
|
49
|
+
integer: 'number',
|
|
50
|
+
decimal: 'number',
|
|
51
|
+
boolean: 'boolean',
|
|
52
|
+
datetime: 'Date',
|
|
53
|
+
date: 'Date',
|
|
54
|
+
id: 'number',
|
|
55
|
+
money: 'Money',
|
|
56
|
+
json: 'any',
|
|
57
|
+
array: 'any[]',
|
|
58
|
+
object: 'object'
|
|
59
|
+
};
|
|
60
|
+
this.availableAggregates = new Map();
|
|
61
|
+
}
|
|
62
|
+
mapType(yamlType) {
|
|
63
|
+
if (this.availableAggregates.has(yamlType)) {
|
|
64
|
+
return yamlType;
|
|
65
|
+
}
|
|
66
|
+
return this.typeMapping[yamlType] || 'any';
|
|
67
|
+
}
|
|
68
|
+
capitalize(str) {
|
|
69
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
70
|
+
}
|
|
71
|
+
generateDefaultHandlerMethod(modelName, actionName, aggregateConfig, childInfo) {
|
|
72
|
+
const entityLower = modelName.toLowerCase();
|
|
73
|
+
const storeName = `${entityLower}Store`;
|
|
74
|
+
switch (actionName) {
|
|
75
|
+
case 'list':
|
|
76
|
+
return ` async list(page: number = 1, limit: number = 20): Promise<{ items: ${modelName}[]; total: number; page: number; limit: number }> {
|
|
77
|
+
const [items, total] = await Promise.all([
|
|
78
|
+
this.${storeName}.getAll(page, limit),
|
|
79
|
+
this.${storeName}.count()
|
|
80
|
+
]);
|
|
81
|
+
return { items, total, page, limit };
|
|
82
|
+
}`;
|
|
83
|
+
case 'get':
|
|
84
|
+
return ` async get(id: number): Promise<${modelName}> {
|
|
85
|
+
const ${entityLower} = await this.${storeName}.getById(id);
|
|
86
|
+
if (!${entityLower}) {
|
|
87
|
+
throw new Error('${modelName} not found');
|
|
88
|
+
}
|
|
89
|
+
return ${entityLower};
|
|
90
|
+
}`;
|
|
91
|
+
case 'create': {
|
|
92
|
+
// Generate constructor args (ownerId for root, parentIdField for child)
|
|
93
|
+
const firstArgField = childInfo ? childInfo.parentIdField : 'ownerId';
|
|
94
|
+
const fields = Object.entries(aggregateConfig.fields)
|
|
95
|
+
.filter(([fieldName, fieldConfig]) => !fieldConfig.auto && fieldName !== 'id')
|
|
96
|
+
.sort((a, b) => {
|
|
97
|
+
const aRequired = a[1].required !== false;
|
|
98
|
+
const bRequired = b[1].required !== false;
|
|
99
|
+
if (aRequired === bRequired)
|
|
100
|
+
return 0;
|
|
101
|
+
return aRequired ? -1 : 1;
|
|
102
|
+
});
|
|
103
|
+
const fieldArgs = fields
|
|
104
|
+
.map(([fieldName]) => `input.${fieldName}`)
|
|
105
|
+
.join(', ');
|
|
106
|
+
const constructorArgs = `input.${firstArgField}, ${fieldArgs}`;
|
|
107
|
+
return ` async create(input: any): Promise<${modelName}> {
|
|
108
|
+
const ${entityLower} = new ${modelName}(0, ${constructorArgs});
|
|
109
|
+
return await this.${storeName}.insert(${entityLower});
|
|
110
|
+
}`;
|
|
111
|
+
}
|
|
112
|
+
case 'update':
|
|
113
|
+
const setterCalls = Object.entries(aggregateConfig.fields)
|
|
114
|
+
.filter(([fieldName, fieldConfig]) => !fieldConfig.auto && fieldName !== 'id')
|
|
115
|
+
.map(([fieldName]) => {
|
|
116
|
+
const methodName = `set${this.capitalize(fieldName)}`;
|
|
117
|
+
return ` if (input.${fieldName} !== undefined) {
|
|
118
|
+
existing${modelName}.${methodName}(input.${fieldName});
|
|
119
|
+
}`;
|
|
120
|
+
})
|
|
121
|
+
.join('\n');
|
|
122
|
+
return ` async update(id: number, input: any): Promise<${modelName}> {
|
|
123
|
+
const existing${modelName} = await this.${storeName}.getById(id);
|
|
124
|
+
if (!existing${modelName}) {
|
|
125
|
+
throw new Error('${modelName} not found');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
${setterCalls}
|
|
129
|
+
|
|
130
|
+
return await this.${storeName}.update(id, existing${modelName});
|
|
131
|
+
}`;
|
|
132
|
+
case 'delete':
|
|
133
|
+
return ` async delete(id: number): Promise<{ success: boolean; message: string }> {
|
|
134
|
+
const success = await this.${storeName}.softDelete(id);
|
|
135
|
+
if (!success) {
|
|
136
|
+
throw new Error('${modelName} not found or could not be deleted');
|
|
137
|
+
}
|
|
138
|
+
return { success: true, message: '${modelName} deleted successfully' };
|
|
139
|
+
}`;
|
|
140
|
+
default:
|
|
141
|
+
return ` async ${actionName}(input: any): Promise<any> {
|
|
142
|
+
// TODO: Implement default ${actionName} handler
|
|
143
|
+
throw new Error('Not implemented');
|
|
144
|
+
}`;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
generateCustomHandlerMethod(modelName, handlerName) {
|
|
148
|
+
return ` async ${handlerName}(result: any, input: any): Promise<any> {
|
|
149
|
+
// TODO: Implement custom ${handlerName} handler
|
|
150
|
+
// This method receives the result from the previous handler (or null if first)
|
|
151
|
+
// and the input context
|
|
152
|
+
return result;
|
|
153
|
+
}`;
|
|
154
|
+
}
|
|
155
|
+
collectHandlers(useCases) {
|
|
156
|
+
const handlers = new Set();
|
|
157
|
+
Object.values(useCases).forEach(useCaseConfig => {
|
|
158
|
+
useCaseConfig.handlers.forEach(handler => {
|
|
159
|
+
handlers.add(handler);
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
return handlers;
|
|
163
|
+
}
|
|
164
|
+
generateListByParentMethod(modelName, childInfo) {
|
|
165
|
+
if (!childInfo)
|
|
166
|
+
return '';
|
|
167
|
+
const storeVar = `${modelName.toLowerCase()}Store`;
|
|
168
|
+
return `
|
|
169
|
+
async listByParent(parentId: number): Promise<${modelName}[]> {
|
|
170
|
+
return await this.${storeVar}.getByParentId(parentId);
|
|
171
|
+
}`;
|
|
172
|
+
}
|
|
173
|
+
generateGetResourceOwnerMethod(modelName) {
|
|
174
|
+
const storeVar = `${modelName.toLowerCase()}Store`;
|
|
175
|
+
return `
|
|
176
|
+
/**
|
|
177
|
+
* Get the owner ID of a resource by its ID.
|
|
178
|
+
* Used for pre-mutation authorization checks.
|
|
179
|
+
*/
|
|
180
|
+
async getResourceOwner(id: number): Promise<number | null> {
|
|
181
|
+
return await this.${storeVar}.getResourceOwner(id);
|
|
182
|
+
}`;
|
|
183
|
+
}
|
|
184
|
+
generateService(modelName, useCases, aggregateConfig, childInfo) {
|
|
185
|
+
const serviceName = `${modelName}Service`;
|
|
186
|
+
const storeName = `${modelName}Store`;
|
|
187
|
+
const storeVar = `${modelName.toLowerCase()}Store`;
|
|
188
|
+
// Collect all unique handlers
|
|
189
|
+
const handlers = this.collectHandlers(useCases);
|
|
190
|
+
// Generate methods for each handler
|
|
191
|
+
const methods = [];
|
|
192
|
+
handlers.forEach(handler => {
|
|
193
|
+
if (handler.startsWith('default:')) {
|
|
194
|
+
const actionName = handler.replace('default:', '');
|
|
195
|
+
methods.push(this.generateDefaultHandlerMethod(modelName, actionName, aggregateConfig, childInfo));
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
methods.push(this.generateCustomHandlerMethod(modelName, handler));
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
const listByParentMethod = this.generateListByParentMethod(modelName, childInfo);
|
|
202
|
+
if (listByParentMethod) {
|
|
203
|
+
methods.push(listByParentMethod);
|
|
204
|
+
}
|
|
205
|
+
const getResourceOwnerMethod = this.generateGetResourceOwnerMethod(modelName);
|
|
206
|
+
if (getResourceOwnerMethod) {
|
|
207
|
+
methods.push(getResourceOwnerMethod);
|
|
208
|
+
}
|
|
209
|
+
return `import { ${modelName} } from '../../domain/entities/${modelName}';
|
|
210
|
+
import { ${storeName} } from '../../infrastructure/stores/${storeName}';
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Service layer for ${modelName}
|
|
214
|
+
* Contains business logic handlers that can be composed in use cases
|
|
215
|
+
*/
|
|
216
|
+
export class ${serviceName} {
|
|
217
|
+
constructor(
|
|
218
|
+
private ${storeVar}: ${storeName}
|
|
219
|
+
) {}
|
|
220
|
+
|
|
221
|
+
${methods.join('\n\n')}
|
|
222
|
+
}`;
|
|
223
|
+
}
|
|
224
|
+
generateFromConfig(config) {
|
|
225
|
+
const result = {};
|
|
226
|
+
// Collect all aggregates
|
|
227
|
+
if (config.domain.aggregates) {
|
|
228
|
+
Object.entries(config.domain.aggregates).forEach(([name, aggConfig]) => {
|
|
229
|
+
this.availableAggregates.set(name, aggConfig);
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
const childEntityMap = (0, childEntityUtils_1.buildChildEntityMap)(config);
|
|
233
|
+
// Generate a Service file for each model
|
|
234
|
+
Object.entries(config.useCases).forEach(([modelName, useCases]) => {
|
|
235
|
+
const aggregateConfig = this.availableAggregates.get(modelName);
|
|
236
|
+
if (!aggregateConfig) {
|
|
237
|
+
console.warn(`Warning: No aggregate found for model ${modelName}`);
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
const childInfo = childEntityMap.get(modelName);
|
|
241
|
+
result[modelName] = this.generateService(modelName, useCases, aggregateConfig, childInfo);
|
|
242
|
+
});
|
|
243
|
+
return result;
|
|
244
|
+
}
|
|
245
|
+
generateFromYamlFile(yamlFilePath) {
|
|
246
|
+
const yamlContent = fs.readFileSync(yamlFilePath, 'utf8');
|
|
247
|
+
const config = (0, yaml_1.parse)(yamlContent);
|
|
248
|
+
if (!(0, configTypes_1.isNewModuleConfig)(config)) {
|
|
249
|
+
throw new Error('Configuration does not match new module format. Expected useCases structure.');
|
|
250
|
+
}
|
|
251
|
+
return this.generateFromConfig(config);
|
|
252
|
+
}
|
|
253
|
+
async generateAndSaveFiles(yamlFilePath, moduleDir, opts) {
|
|
254
|
+
const servicesByModel = this.generateFromYamlFile(yamlFilePath);
|
|
255
|
+
const servicesDir = path.join(moduleDir, 'application', 'services');
|
|
256
|
+
fs.mkdirSync(servicesDir, { recursive: true });
|
|
257
|
+
for (const [modelName, code] of Object.entries(servicesByModel)) {
|
|
258
|
+
const filePath = path.join(servicesDir, `${modelName}Service.ts`);
|
|
259
|
+
// eslint-disable-next-line no-await-in-loop
|
|
260
|
+
await (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) });
|
|
261
|
+
}
|
|
262
|
+
// eslint-disable-next-line no-console
|
|
263
|
+
console.log('\n' + colors_1.colors.green('Service files generated successfully!') + '\n');
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
exports.NewServiceGenerator = NewServiceGenerator;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { NewModuleConfig } from '../types/configTypes';
|
|
2
|
+
export declare class NewStoreGenerator {
|
|
3
|
+
private rowTypeMapping;
|
|
4
|
+
private availableValueObjects;
|
|
5
|
+
private capitalize;
|
|
6
|
+
private isValueObjectType;
|
|
7
|
+
private getValueObjectName;
|
|
8
|
+
private getValueObjectConfig;
|
|
9
|
+
/**
|
|
10
|
+
* Sort fields the same way as the domain layer generator:
|
|
11
|
+
* Required fields first, then optional fields.
|
|
12
|
+
* This ensures rowToModel parameter order matches entity constructor.
|
|
13
|
+
*/
|
|
14
|
+
private sortFieldsForConstructor;
|
|
15
|
+
/**
|
|
16
|
+
* Gets the type name for a value object field (for casting).
|
|
17
|
+
* For enum fields, returns the generated type name (e.g., BreedName).
|
|
18
|
+
* For other fields, returns the basic TypeScript type.
|
|
19
|
+
*/
|
|
20
|
+
private getValueObjectFieldTypeName;
|
|
21
|
+
private mapTypeToRowType;
|
|
22
|
+
private replaceTemplateVars;
|
|
23
|
+
private generateRowFields;
|
|
24
|
+
private generateFieldNamesStr;
|
|
25
|
+
private generateRowToModelMapping;
|
|
26
|
+
private generateInsertDataMapping;
|
|
27
|
+
private generateUpdateDataMapping;
|
|
28
|
+
private generateUpdateFieldsArray;
|
|
29
|
+
private generateValueObjectImports;
|
|
30
|
+
private generateGetByParentIdMethod;
|
|
31
|
+
private generateGetResourceOwnerMethod;
|
|
32
|
+
private generateStore;
|
|
33
|
+
generateFromConfig(config: NewModuleConfig): Record<string, string>;
|
|
34
|
+
generateFromYamlFile(yamlFilePath: string): Record<string, string>;
|
|
35
|
+
generateAndSaveFiles(yamlFilePath: string, moduleDir: string, opts?: {
|
|
36
|
+
force?: boolean;
|
|
37
|
+
skipOnConflict?: boolean;
|
|
38
|
+
}): Promise<void>;
|
|
39
|
+
}
|