@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.
Files changed (60) hide show
  1. package/CHANGELOG.md +18 -609
  2. package/README.md +623 -427
  3. package/dist/cli.js +2 -1
  4. package/dist/commands/commit.js +25 -42
  5. package/dist/commands/createApp.js +1 -0
  6. package/dist/commands/createModule.js +151 -45
  7. package/dist/commands/diff.js +27 -40
  8. package/dist/commands/generateAll.js +141 -291
  9. package/dist/commands/migrateCommit.js +6 -18
  10. package/dist/generators/controllerGenerator.d.ts +50 -19
  11. package/dist/generators/controllerGenerator.js +588 -331
  12. package/dist/generators/domainLayerGenerator.d.ts +21 -0
  13. package/dist/generators/domainLayerGenerator.js +286 -0
  14. package/dist/generators/dtoGenerator.d.ts +21 -0
  15. package/dist/generators/dtoGenerator.js +523 -0
  16. package/dist/generators/serviceGenerator.d.ts +22 -51
  17. package/dist/generators/serviceGenerator.js +345 -568
  18. package/dist/generators/storeGenerator.d.ts +39 -32
  19. package/dist/generators/storeGenerator.js +396 -236
  20. package/dist/generators/templateGenerator.d.ts +21 -21
  21. package/dist/generators/templateGenerator.js +393 -268
  22. package/dist/generators/templates/appTemplates.d.ts +3 -1
  23. package/dist/generators/templates/appTemplates.js +16 -11
  24. package/dist/generators/templates/data/appYamlTemplate +5 -2
  25. package/dist/generators/templates/data/cursorRulesTemplate +315 -221
  26. package/dist/generators/templates/data/frontendScriptTemplate +56 -15
  27. package/dist/generators/templates/data/mainViewTemplate +2 -1
  28. package/dist/generators/templates/data/systemTsTemplate +5 -0
  29. package/dist/generators/templates/index.d.ts +0 -3
  30. package/dist/generators/templates/index.js +0 -3
  31. package/dist/generators/templates/storeTemplates.d.ts +1 -5
  32. package/dist/generators/templates/storeTemplates.js +84 -224
  33. package/dist/generators/useCaseGenerator.d.ts +13 -0
  34. package/dist/generators/useCaseGenerator.js +191 -0
  35. package/dist/types/configTypes.d.ts +149 -0
  36. package/dist/types/configTypes.js +10 -0
  37. package/dist/utils/childEntityUtils.d.ts +18 -0
  38. package/dist/utils/childEntityUtils.js +78 -0
  39. package/dist/utils/commandUtils.d.ts +43 -0
  40. package/dist/utils/commandUtils.js +124 -0
  41. package/dist/utils/commitUtils.d.ts +4 -1
  42. package/dist/utils/constants.d.ts +10 -0
  43. package/dist/utils/constants.js +13 -1
  44. package/dist/utils/diResolver.d.ts +32 -0
  45. package/dist/utils/diResolver.js +204 -0
  46. package/dist/utils/typeUtils.d.ts +23 -0
  47. package/dist/utils/typeUtils.js +77 -0
  48. package/package.json +7 -3
  49. package/dist/generators/domainModelGenerator.d.ts +0 -41
  50. package/dist/generators/domainModelGenerator.js +0 -242
  51. package/dist/generators/templates/controllerTemplates.d.ts +0 -43
  52. package/dist/generators/templates/controllerTemplates.js +0 -82
  53. package/dist/generators/templates/serviceTemplates.d.ts +0 -16
  54. package/dist/generators/templates/serviceTemplates.js +0 -59
  55. package/dist/generators/templates/validationTemplates.d.ts +0 -25
  56. package/dist/generators/templates/validationTemplates.js +0 -66
  57. package/dist/generators/templates/viewTemplates.d.ts +0 -25
  58. package/dist/generators/templates/viewTemplates.js +0 -491
  59. package/dist/generators/validationGenerator.d.ts +0 -29
  60. package/dist/generators/validationGenerator.js +0 -250
@@ -0,0 +1,21 @@
1
+ import { ModuleConfig } from '../types/configTypes';
2
+ export declare class DomainLayerGenerator {
3
+ private availableAggregates;
4
+ private availableValueObjects;
5
+ private mapType;
6
+ private getDefaultValue;
7
+ private generateValueObject;
8
+ private generateAggregate;
9
+ generateFromConfig(config: ModuleConfig): Record<string, {
10
+ code: string;
11
+ type: 'entity' | 'valueObject';
12
+ }>;
13
+ generateFromYamlFile(yamlFilePath: string): Record<string, {
14
+ code: string;
15
+ type: 'entity' | 'valueObject';
16
+ }>;
17
+ generateAndSaveFiles(yamlFilePath: string, moduleDir: string, opts?: {
18
+ force?: boolean;
19
+ skipOnConflict?: boolean;
20
+ }): Promise<void>;
21
+ }
@@ -0,0 +1,286 @@
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.DomainLayerGenerator = 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
+ const typeUtils_1 = require("../utils/typeUtils");
45
+ class DomainLayerGenerator {
46
+ constructor() {
47
+ this.availableAggregates = new Set();
48
+ this.availableValueObjects = new Set();
49
+ }
50
+ mapType(yamlType) {
51
+ return (0, typeUtils_1.mapType)(yamlType, this.availableAggregates, this.availableValueObjects);
52
+ }
53
+ getDefaultValue(type) {
54
+ switch (type) {
55
+ case 'datetime':
56
+ case 'date':
57
+ return 'new Date()';
58
+ case 'string':
59
+ return "''";
60
+ case 'number':
61
+ case 'integer':
62
+ case 'decimal':
63
+ return '0';
64
+ case 'boolean':
65
+ return 'false';
66
+ case 'array':
67
+ return '[]';
68
+ case 'object':
69
+ case 'json':
70
+ return '{}';
71
+ default:
72
+ return 'undefined';
73
+ }
74
+ }
75
+ generateValueObject(name, config) {
76
+ const fields = Object.entries(config.fields);
77
+ // Collect type definitions for enum fields
78
+ const typeDefinitions = [];
79
+ // Generate constructor parameters
80
+ const constructorParams = fields.map(([fieldName, fieldConfig]) => {
81
+ if (typeof fieldConfig === 'object' && 'values' in fieldConfig) {
82
+ // Enum type - generate a type alias
83
+ const typeName = `${name}${(0, typeUtils_1.capitalize)(fieldName)}`;
84
+ const uniqueValues = [...new Set(fieldConfig.values)]; // dedupe
85
+ const enumValues = uniqueValues.map(v => `'${v}'`).join(' | ');
86
+ typeDefinitions.push(`export type ${typeName} = ${enumValues};`);
87
+ return `public ${fieldName}: ${typeName}`;
88
+ }
89
+ else {
90
+ const tsType = this.mapType(fieldConfig.type);
91
+ return `public ${fieldName}: ${tsType}`;
92
+ }
93
+ }).join(',\n ');
94
+ // Generate validation logic
95
+ const validations = [];
96
+ fields.forEach(([fieldName, fieldConfig]) => {
97
+ if (typeof fieldConfig === 'object' && 'constraints' in fieldConfig && fieldConfig.constraints) {
98
+ const constraints = fieldConfig.constraints;
99
+ if (constraints.min !== undefined) {
100
+ validations.push(` if (this.${fieldName} < ${constraints.min}) {
101
+ throw new Error('${name}.${fieldName} must be at least ${constraints.min}');
102
+ }`);
103
+ }
104
+ if (constraints.max !== undefined) {
105
+ validations.push(` if (this.${fieldName} > ${constraints.max}) {
106
+ throw new Error('${name}.${fieldName} must be at most ${constraints.max}');
107
+ }`);
108
+ }
109
+ if (constraints.pattern) {
110
+ validations.push(` if (!/${constraints.pattern}/.test(String(this.${fieldName}))) {
111
+ throw new Error('${name}.${fieldName} does not match required pattern');
112
+ }`);
113
+ }
114
+ }
115
+ if (typeof fieldConfig === 'object' && 'values' in fieldConfig) {
116
+ const uniqueValues = [...new Set(fieldConfig.values)]; // dedupe
117
+ const values = uniqueValues.map(v => `'${v}'`).join(', ');
118
+ validations.push(` if (![${values}].includes(this.${fieldName})) {
119
+ throw new Error(\`${name}.${fieldName} must be one of: ${values}\`);
120
+ }`);
121
+ }
122
+ });
123
+ const validationCode = validations.length > 0 ? `\n this.validate();\n }\n\n private validate(): void {\n${validations.join('\n')}\n }` : '\n }';
124
+ const typeDefsCode = typeDefinitions.length > 0 ? typeDefinitions.join('\n') + '\n\n' : '';
125
+ return `${typeDefsCode}export class ${name} {
126
+ public constructor(
127
+ ${constructorParams}
128
+ ) {${validationCode}
129
+ }`;
130
+ }
131
+ generateAggregate(name, config, allAggregates, childInfo) {
132
+ const fields = Object.entries(config.fields);
133
+ // Determine which entities are contained in this aggregate
134
+ const containedEntities = config.entities || [];
135
+ // Generate imports for contained entities
136
+ const entityImports = containedEntities
137
+ .filter(entityName => entityName !== name && allAggregates[entityName])
138
+ .map(entityName => `import { ${entityName} } from './${entityName}';`)
139
+ .join('\n');
140
+ // Generate imports for value objects used in fields
141
+ const valueObjectImports = fields
142
+ .filter(([, fieldConfig]) => this.availableValueObjects.has((0, typeUtils_1.capitalize)(fieldConfig.type)))
143
+ .map(([, fieldConfig]) => {
144
+ const voName = (0, typeUtils_1.capitalize)(fieldConfig.type);
145
+ return `import { ${voName} } from '../valueObjects/${voName}';`;
146
+ })
147
+ .filter((imp, idx, arr) => arr.indexOf(imp) === idx) // dedupe
148
+ .join('\n');
149
+ // Generate imports for aggregate references in fields (e.g. idea: { type: Idea })
150
+ const aggregateRefImports = fields
151
+ .filter(([, fieldConfig]) => (0, typeUtils_1.isAggregateReference)(fieldConfig.type, this.availableAggregates) &&
152
+ fieldConfig.type !== name)
153
+ .map(([, fieldConfig]) => `import { ${fieldConfig.type} } from './${fieldConfig.type}';`)
154
+ .filter((imp, idx, arr) => arr.indexOf(imp) === idx)
155
+ .join('\n');
156
+ const imports = [entityImports, valueObjectImports, aggregateRefImports].filter(Boolean).join('\n');
157
+ // Generate constructor parameters: id, then ownerId (root) or parentId field (child)
158
+ const constructorParams = ['public id: number'];
159
+ if (childInfo) {
160
+ constructorParams.push(`public ${childInfo.parentIdField}: number`);
161
+ }
162
+ else {
163
+ constructorParams.push('public ownerId: number');
164
+ }
165
+ // Sort fields: required first, then optional
166
+ // Fields are required by default unless required: false
167
+ // Aggregate references are always treated as optional (store can't populate them from FK alone)
168
+ const sortedFields = fields.sort((a, b) => {
169
+ const aIsAggRef = (0, typeUtils_1.isAggregateReference)(a[1].type, this.availableAggregates);
170
+ const bIsAggRef = (0, typeUtils_1.isAggregateReference)(b[1].type, this.availableAggregates);
171
+ const aRequired = a[1].required !== false && !a[1].auto && !aIsAggRef;
172
+ const bRequired = b[1].required !== false && !b[1].auto && !bIsAggRef;
173
+ if (aRequired === bRequired)
174
+ return 0;
175
+ return aRequired ? -1 : 1;
176
+ });
177
+ const enumTypeDefinitions = [];
178
+ const enumTypeNames = {};
179
+ sortedFields.forEach(([fieldName, fieldConfig]) => {
180
+ if (fieldConfig.type === 'enum' && fieldConfig.values && fieldConfig.values.length > 0) {
181
+ const typeName = `${name}${(0, typeUtils_1.capitalize)(fieldName)}`;
182
+ const uniqueValues = [...new Set(fieldConfig.values)];
183
+ const enumValues = uniqueValues.map(v => `'${v}'`).join(' | ');
184
+ enumTypeDefinitions.push(`export type ${typeName} = ${enumValues};`);
185
+ enumTypeNames[fieldName] = typeName;
186
+ }
187
+ });
188
+ sortedFields.forEach(([fieldName, fieldConfig]) => {
189
+ const isAggRef = (0, typeUtils_1.isAggregateReference)(fieldConfig.type, this.availableAggregates);
190
+ const tsType = enumTypeNames[fieldName] || this.mapType(fieldConfig.type);
191
+ const isOptional = fieldConfig.required === false || isAggRef;
192
+ const hasDefault = fieldConfig.auto;
193
+ let param = `public ${fieldName}`;
194
+ if (isOptional && !hasDefault) {
195
+ param += '?';
196
+ }
197
+ param += `: ${tsType}`;
198
+ if (hasDefault) {
199
+ param += ` = ${this.getDefaultValue(fieldConfig.type)}`;
200
+ }
201
+ constructorParams.push(param);
202
+ });
203
+ const constructorParamsStr = constructorParams.join(',\n ');
204
+ // Generate setter methods
205
+ const setterMethods = sortedFields
206
+ .filter(([fieldName, fieldConfig]) => !fieldConfig.auto && fieldName !== 'id')
207
+ .map(([fieldName, fieldConfig]) => {
208
+ const isAggRef = (0, typeUtils_1.isAggregateReference)(fieldConfig.type, this.availableAggregates);
209
+ const tsType = enumTypeNames[fieldName] || this.mapType(fieldConfig.type);
210
+ const methodName = `set${(0, typeUtils_1.capitalize)(fieldName)}`;
211
+ const isOptional = fieldConfig.required === false || isAggRef;
212
+ return `
213
+ ${methodName}(${fieldName}: ${tsType}${isOptional ? ' | undefined' : ''}): void {
214
+ this.${fieldName} = ${fieldName};
215
+ }`;
216
+ })
217
+ .join('\n');
218
+ const rootComment = config.root ? '// Aggregate Root\n' : '';
219
+ const enumTypeDefsCode = enumTypeDefinitions.length > 0 ? enumTypeDefinitions.join('\n') + '\n\n' : '';
220
+ return `${imports ? imports + '\n\n' : ''}${enumTypeDefsCode}${rootComment}export class ${name} {
221
+ public constructor(
222
+ ${constructorParamsStr}
223
+ ) { }
224
+ ${setterMethods}
225
+ }`;
226
+ }
227
+ generateFromConfig(config) {
228
+ const result = {};
229
+ // First pass: collect all aggregate and value object names
230
+ if (config.domain.aggregates) {
231
+ Object.keys(config.domain.aggregates).forEach(name => {
232
+ this.availableAggregates.add(name);
233
+ });
234
+ }
235
+ if (config.domain.valueObjects) {
236
+ Object.keys(config.domain.valueObjects).forEach(name => {
237
+ this.availableValueObjects.add(name);
238
+ });
239
+ }
240
+ // Generate value objects first (they may be used by aggregates)
241
+ if (config.domain.valueObjects) {
242
+ Object.entries(config.domain.valueObjects).forEach(([name, voConfig]) => {
243
+ result[name] = {
244
+ code: this.generateValueObject(name, voConfig),
245
+ type: 'valueObject'
246
+ };
247
+ });
248
+ }
249
+ // Generate aggregates
250
+ const childEntityMap = (0, childEntityUtils_1.buildChildEntityMap)(config);
251
+ if (config.domain.aggregates) {
252
+ Object.entries(config.domain.aggregates).forEach(([name, aggConfig]) => {
253
+ const childInfo = childEntityMap.get(name);
254
+ result[name] = {
255
+ code: this.generateAggregate(name, aggConfig, config.domain.aggregates, childInfo),
256
+ type: 'entity'
257
+ };
258
+ });
259
+ }
260
+ return result;
261
+ }
262
+ generateFromYamlFile(yamlFilePath) {
263
+ const yamlContent = fs.readFileSync(yamlFilePath, 'utf8');
264
+ const config = (0, yaml_1.parse)(yamlContent);
265
+ if (!(0, configTypes_1.isValidModuleConfig)(config)) {
266
+ throw new Error('Configuration does not match new module format. Expected domain.aggregates structure.');
267
+ }
268
+ return this.generateFromConfig(config);
269
+ }
270
+ async generateAndSaveFiles(yamlFilePath, moduleDir, opts) {
271
+ const codeByEntity = this.generateFromYamlFile(yamlFilePath);
272
+ const entitiesDir = path.join(moduleDir, 'domain', 'entities');
273
+ const valueObjectsDir = path.join(moduleDir, 'domain', 'valueObjects');
274
+ fs.mkdirSync(entitiesDir, { recursive: true });
275
+ fs.mkdirSync(valueObjectsDir, { recursive: true });
276
+ for (const [name, { code, type }] of Object.entries(codeByEntity)) {
277
+ const outputDir = type === 'valueObject' ? valueObjectsDir : entitiesDir;
278
+ const filePath = path.join(outputDir, `${name}.ts`);
279
+ // eslint-disable-next-line no-await-in-loop
280
+ 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) });
281
+ }
282
+ // eslint-disable-next-line no-console
283
+ console.log('\n' + colors_1.colors.green('Domain layer files generated successfully!') + '\n');
284
+ }
285
+ }
286
+ exports.DomainLayerGenerator = DomainLayerGenerator;
@@ -0,0 +1,21 @@
1
+ import { ModuleConfig } from '../types/configTypes';
2
+ export declare class DtoGenerator {
3
+ private availableAggregates;
4
+ private availableValueObjects;
5
+ private mapType;
6
+ private isValueObjectType;
7
+ private getValidationCode;
8
+ private getTransformCode;
9
+ private generateInputDto;
10
+ private generateOutputDto;
11
+ /**
12
+ * Collect types that need to be imported for a use case DTO.
13
+ */
14
+ private collectRequiredImports;
15
+ generateFromConfig(config: ModuleConfig): Record<string, string>;
16
+ generateFromYamlFile(yamlFilePath: string): Record<string, string>;
17
+ generateAndSaveFiles(yamlFilePath: string, moduleDir: string, opts?: {
18
+ force?: boolean;
19
+ skipOnConflict?: boolean;
20
+ }): Promise<void>;
21
+ }