@currentjs/gen 0.1.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 +7 -0
- package/LICENSE +56 -0
- package/README.md +686 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +143 -0
- package/dist/commands/commit.d.ts +1 -0
- package/dist/commands/commit.js +153 -0
- package/dist/commands/createApp.d.ts +1 -0
- package/dist/commands/createApp.js +64 -0
- package/dist/commands/createModule.d.ts +1 -0
- package/dist/commands/createModule.js +121 -0
- package/dist/commands/diff.d.ts +1 -0
- package/dist/commands/diff.js +164 -0
- package/dist/commands/generateAll.d.ts +4 -0
- package/dist/commands/generateAll.js +305 -0
- package/dist/commands/infer.d.ts +1 -0
- package/dist/commands/infer.js +179 -0
- package/dist/generators/controllerGenerator.d.ts +20 -0
- package/dist/generators/controllerGenerator.js +280 -0
- package/dist/generators/domainModelGenerator.d.ts +33 -0
- package/dist/generators/domainModelGenerator.js +175 -0
- package/dist/generators/serviceGenerator.d.ts +39 -0
- package/dist/generators/serviceGenerator.js +379 -0
- package/dist/generators/storeGenerator.d.ts +31 -0
- package/dist/generators/storeGenerator.js +191 -0
- package/dist/generators/templateGenerator.d.ts +11 -0
- package/dist/generators/templateGenerator.js +143 -0
- package/dist/generators/templates/appTemplates.d.ts +27 -0
- package/dist/generators/templates/appTemplates.js +1621 -0
- package/dist/generators/templates/controllerTemplates.d.ts +43 -0
- package/dist/generators/templates/controllerTemplates.js +82 -0
- package/dist/generators/templates/index.d.ts +5 -0
- package/dist/generators/templates/index.js +21 -0
- package/dist/generators/templates/serviceTemplates.d.ts +15 -0
- package/dist/generators/templates/serviceTemplates.js +54 -0
- package/dist/generators/templates/storeTemplates.d.ts +9 -0
- package/dist/generators/templates/storeTemplates.js +260 -0
- package/dist/generators/templates/validationTemplates.d.ts +25 -0
- package/dist/generators/templates/validationTemplates.js +66 -0
- package/dist/generators/templates/viewTemplates.d.ts +16 -0
- package/dist/generators/templates/viewTemplates.js +359 -0
- package/dist/generators/validationGenerator.d.ts +24 -0
- package/dist/generators/validationGenerator.js +199 -0
- package/dist/utils/cliUtils.d.ts +6 -0
- package/dist/utils/cliUtils.js +71 -0
- package/dist/utils/colors.d.ts +26 -0
- package/dist/utils/colors.js +80 -0
- package/dist/utils/commitUtils.d.ts +46 -0
- package/dist/utils/commitUtils.js +377 -0
- package/dist/utils/constants.d.ts +52 -0
- package/dist/utils/constants.js +64 -0
- package/dist/utils/generationRegistry.d.ts +25 -0
- package/dist/utils/generationRegistry.js +192 -0
- package/howto.md +556 -0
- package/package.json +44 -0
|
@@ -0,0 +1,379 @@
|
|
|
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.ServiceGenerator = void 0;
|
|
37
|
+
const yaml_1 = require("yaml");
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
const serviceTemplates_1 = require("./templates/serviceTemplates");
|
|
41
|
+
const generationRegistry_1 = require("../utils/generationRegistry");
|
|
42
|
+
const colors_1 = require("../utils/colors");
|
|
43
|
+
const constants_1 = require("../utils/constants");
|
|
44
|
+
class ServiceGenerator {
|
|
45
|
+
hasPermissions(config) {
|
|
46
|
+
if (config.modules) {
|
|
47
|
+
return Object.values(config.modules).some(module => module.permissions && module.permissions.length > 0);
|
|
48
|
+
}
|
|
49
|
+
const module = config;
|
|
50
|
+
return !!(module.permissions && module.permissions.length > 0);
|
|
51
|
+
}
|
|
52
|
+
getActionPermissions(moduleName, moduleConfig) {
|
|
53
|
+
if (!moduleConfig.permissions || !moduleConfig.actions) {
|
|
54
|
+
return {};
|
|
55
|
+
}
|
|
56
|
+
const actionPermissions = {};
|
|
57
|
+
// Initialize all actions with empty permissions
|
|
58
|
+
Object.keys(moduleConfig.actions).forEach(action => {
|
|
59
|
+
actionPermissions[action] = [];
|
|
60
|
+
});
|
|
61
|
+
// Fill in permissions for each action
|
|
62
|
+
moduleConfig.permissions.forEach(permission => {
|
|
63
|
+
permission.actions.forEach(action => {
|
|
64
|
+
if (actionPermissions[action]) {
|
|
65
|
+
actionPermissions[action].push(permission.role);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
return actionPermissions;
|
|
70
|
+
}
|
|
71
|
+
generatePermissionCheck(action, roles, entityName) {
|
|
72
|
+
if (roles.length === 0 || roles.includes('all')) {
|
|
73
|
+
return '';
|
|
74
|
+
}
|
|
75
|
+
if (roles.includes('none')) {
|
|
76
|
+
return " throw new Error('This action is not permitted');";
|
|
77
|
+
}
|
|
78
|
+
const nonOwnerRoles = roles.filter(role => !['all', 'none', 'owner'].includes(role));
|
|
79
|
+
const rolesArray = nonOwnerRoles.map(role => `'${role}'`).join(', ');
|
|
80
|
+
// Special-case: list action with owner → rely on store-level filtering; only enforce static role checks for other roles
|
|
81
|
+
if (action === 'list' && roles.includes('owner')) {
|
|
82
|
+
if (nonOwnerRoles.length === 0) {
|
|
83
|
+
return '';
|
|
84
|
+
}
|
|
85
|
+
return serviceTemplates_1.serviceTemplates.permissionCheck
|
|
86
|
+
.replace(/{{REQUIRED_ROLES}}/g, roles.join(', '))
|
|
87
|
+
.replace(/{{ACTION_NAME}}/g, action)
|
|
88
|
+
.replace(/{{ROLES_ARRAY}}/g, rolesArray);
|
|
89
|
+
}
|
|
90
|
+
// For resource-based owner permission (non-list), fetch resource first and verify ownership
|
|
91
|
+
if (roles.includes('owner')) {
|
|
92
|
+
const isResourceBased = ['get', 'update', 'delete'].includes(action);
|
|
93
|
+
if (!isResourceBased) {
|
|
94
|
+
// Not a resource-based action; fall back to static role checks only (if any non-owner roles)
|
|
95
|
+
if (nonOwnerRoles.length === 0) {
|
|
96
|
+
return '';
|
|
97
|
+
}
|
|
98
|
+
return serviceTemplates_1.serviceTemplates.permissionCheck
|
|
99
|
+
.replace(/{{REQUIRED_ROLES}}/g, roles.join(', '))
|
|
100
|
+
.replace(/{{ACTION_NAME}}/g, action)
|
|
101
|
+
.replace(/{{ROLES_ARRAY}}/g, rolesArray);
|
|
102
|
+
}
|
|
103
|
+
const entityLower = entityName.toLowerCase();
|
|
104
|
+
const resourceIdParam = this.getResourceIdForAction(action);
|
|
105
|
+
const notFoundMessage = `${entityName} not found`;
|
|
106
|
+
// If there are additional roles besides owner, allow them to bypass owner check
|
|
107
|
+
const maybeRoleBypass = nonOwnerRoles.length > 0
|
|
108
|
+
? ` const allowedRoles = [${rolesArray}];
|
|
109
|
+
if (allowedRoles.includes(user.role)) {
|
|
110
|
+
// Explicit role allowed; skip owner check
|
|
111
|
+
} else {
|
|
112
|
+
const ${entityLower} = await this.${entityLower}Store.getById(${resourceIdParam});
|
|
113
|
+
if (!${entityLower}) {
|
|
114
|
+
throw new Error('${notFoundMessage}');
|
|
115
|
+
}
|
|
116
|
+
if ((${entityLower} as any).userId !== user.id) {
|
|
117
|
+
throw new Error('You can only access your own resources');
|
|
118
|
+
}
|
|
119
|
+
}`
|
|
120
|
+
: ` const ${entityLower} = await this.${entityLower}Store.getById(${resourceIdParam});
|
|
121
|
+
if (!${entityLower}) {
|
|
122
|
+
throw new Error('${notFoundMessage}');
|
|
123
|
+
}
|
|
124
|
+
if ((${entityLower} as any).userId !== user.id) {
|
|
125
|
+
throw new Error('You can only access your own resources');
|
|
126
|
+
}`;
|
|
127
|
+
return maybeRoleBypass;
|
|
128
|
+
}
|
|
129
|
+
// Static role check for non-owner roles
|
|
130
|
+
return serviceTemplates_1.serviceTemplates.permissionCheck
|
|
131
|
+
.replace(/{{REQUIRED_ROLES}}/g, roles.join(', '))
|
|
132
|
+
.replace(/{{ACTION_NAME}}/g, action)
|
|
133
|
+
.replace(/{{ROLES_ARRAY}}/g, rolesArray);
|
|
134
|
+
}
|
|
135
|
+
getResourceIdForAction(action) {
|
|
136
|
+
switch (action) {
|
|
137
|
+
case 'get':
|
|
138
|
+
case 'update':
|
|
139
|
+
case 'delete':
|
|
140
|
+
return 'id';
|
|
141
|
+
default:
|
|
142
|
+
return 'id';
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
generateMethodParams(action, entityName) {
|
|
146
|
+
const entityParam = `${entityName.toLowerCase()}Data`;
|
|
147
|
+
switch (action) {
|
|
148
|
+
case 'list':
|
|
149
|
+
return 'page: number = 1, limit: number = 10';
|
|
150
|
+
case 'get':
|
|
151
|
+
return 'id: number';
|
|
152
|
+
case 'create':
|
|
153
|
+
return `${entityParam}: ${entityName}DTO`;
|
|
154
|
+
case 'update':
|
|
155
|
+
return `id: number, ${entityParam}: ${entityName}DTO`;
|
|
156
|
+
case 'delete':
|
|
157
|
+
return 'id: number';
|
|
158
|
+
default:
|
|
159
|
+
return '';
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
generateReturnType(action, entityName) {
|
|
163
|
+
switch (action) {
|
|
164
|
+
case 'list':
|
|
165
|
+
return `${entityName}[]`;
|
|
166
|
+
case 'get':
|
|
167
|
+
case 'create':
|
|
168
|
+
case 'update':
|
|
169
|
+
return entityName;
|
|
170
|
+
case 'delete':
|
|
171
|
+
return '{ success: boolean; message: string }';
|
|
172
|
+
default:
|
|
173
|
+
return 'void';
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
generateMethodImplementation(action, entityName, handlers, moduleName, roles, moduleConfig) {
|
|
177
|
+
if (!handlers || handlers.length === 0) {
|
|
178
|
+
return `// TODO: Implement ${action} method`;
|
|
179
|
+
}
|
|
180
|
+
const implementations = [];
|
|
181
|
+
handlers.forEach(handler => {
|
|
182
|
+
if (handler.startsWith('default:')) {
|
|
183
|
+
const defaultAction = handler.substring(8);
|
|
184
|
+
const template = serviceTemplates_1.serviceTemplates.defaultImplementations[defaultAction];
|
|
185
|
+
if (template) {
|
|
186
|
+
const entityLower = entityName.toLowerCase();
|
|
187
|
+
let processedTemplate = template
|
|
188
|
+
.replace(/{{ENTITY_NAME}}/g, entityName)
|
|
189
|
+
.replace(/{{ENTITY_LOWER}}/g, entityLower);
|
|
190
|
+
// Handle constructor args for create action
|
|
191
|
+
if (defaultAction === 'create') {
|
|
192
|
+
const constructorArgs = this.generateConstructorArgs(moduleConfig, entityName);
|
|
193
|
+
processedTemplate = processedTemplate.replace(/{{CONSTRUCTOR_ARGS}}/g, constructorArgs);
|
|
194
|
+
}
|
|
195
|
+
// Handle setter calls for update action
|
|
196
|
+
if (defaultAction === 'update') {
|
|
197
|
+
const setterCalls = this.generateUpdateSetterCalls(moduleConfig, entityName);
|
|
198
|
+
processedTemplate = processedTemplate.replace(/{{UPDATE_SETTER_CALLS}}/g, setterCalls);
|
|
199
|
+
}
|
|
200
|
+
// Special-case: list action with only owner role → fetch by userId
|
|
201
|
+
if (defaultAction === 'list') {
|
|
202
|
+
const onlyOwner = roles.length > 0 && roles.every(r => r === 'owner');
|
|
203
|
+
if (onlyOwner) {
|
|
204
|
+
processedTemplate = `const ${entityLower}s = await this.${entityLower}Store.getAllByUserId(user.id, page, limit);\n return ${entityLower}s;`;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
implementations.push(processedTemplate);
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
implementations.push(`// TODO: Implement default ${defaultAction} method`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
else if (handler.startsWith(constants_1.PATH_PATTERNS.MODULES_DIRECTIVE)) {
|
|
214
|
+
const functionName = this.extractFunctionName(handler);
|
|
215
|
+
const customImpl = serviceTemplates_1.serviceTemplates.customActionImplementation.replace(/{{CUSTOM_FUNCTION_CALL}}/g, `${functionName}(${this.getMethodCallParams(action)})`);
|
|
216
|
+
implementations.push(customImpl);
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
implementations.push(`// TODO: Implement handler ${handler}`);
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
return implementations.join('\n ');
|
|
223
|
+
}
|
|
224
|
+
extractFunctionName(filePath) {
|
|
225
|
+
const parts = filePath.split('/');
|
|
226
|
+
const fileName = parts[parts.length - 1];
|
|
227
|
+
return fileName;
|
|
228
|
+
}
|
|
229
|
+
getMethodCallParams(action) {
|
|
230
|
+
switch (action) {
|
|
231
|
+
case 'list':
|
|
232
|
+
return 'page, limit';
|
|
233
|
+
case 'get':
|
|
234
|
+
return 'id';
|
|
235
|
+
case 'create':
|
|
236
|
+
return 'userData';
|
|
237
|
+
case 'update':
|
|
238
|
+
return 'id, updates';
|
|
239
|
+
case 'delete':
|
|
240
|
+
return 'id';
|
|
241
|
+
default:
|
|
242
|
+
return '/* custom params */';
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
generateConstructorArgs(moduleConfig, entityName) {
|
|
246
|
+
if (!moduleConfig.models || moduleConfig.models.length === 0) {
|
|
247
|
+
return '';
|
|
248
|
+
}
|
|
249
|
+
const model = moduleConfig.models[0];
|
|
250
|
+
const entityLower = entityName.toLowerCase();
|
|
251
|
+
return model.fields
|
|
252
|
+
.filter(field => !field.auto && field.name !== 'id')
|
|
253
|
+
.map(field => `${entityLower}Data.${field.name}`)
|
|
254
|
+
.join(', ');
|
|
255
|
+
}
|
|
256
|
+
generateUpdateSetterCalls(moduleConfig, entityName) {
|
|
257
|
+
if (!moduleConfig.models || moduleConfig.models.length === 0) {
|
|
258
|
+
return '';
|
|
259
|
+
}
|
|
260
|
+
const model = moduleConfig.models[0];
|
|
261
|
+
const entityLower = entityName.toLowerCase();
|
|
262
|
+
return model.fields
|
|
263
|
+
.filter(field => !field.auto && field.name !== 'id')
|
|
264
|
+
.map(field => {
|
|
265
|
+
const methodName = `set${field.name.charAt(0).toUpperCase() + field.name.slice(1)}`;
|
|
266
|
+
return `if (${entityLower}Data.${field.name} !== undefined) {
|
|
267
|
+
existing${entityName}.${methodName}(${entityLower}Data.${field.name});
|
|
268
|
+
}`;
|
|
269
|
+
})
|
|
270
|
+
.join('\n ');
|
|
271
|
+
}
|
|
272
|
+
replaceTemplateVars(template, variables) {
|
|
273
|
+
let result = template;
|
|
274
|
+
Object.entries(variables).forEach(([key, value]) => {
|
|
275
|
+
const regex = new RegExp(`{{${key}}}`, 'g');
|
|
276
|
+
result = result.replace(regex, String(value));
|
|
277
|
+
});
|
|
278
|
+
return result;
|
|
279
|
+
}
|
|
280
|
+
generateServiceMethod(action, entityName, roles, hasPermissions, handlers, moduleName, moduleConfig) {
|
|
281
|
+
const methodParams = this.generateMethodParams(action, entityName);
|
|
282
|
+
const returnType = this.generateReturnType(action, entityName);
|
|
283
|
+
const permissionCheck = this.generatePermissionCheck(action, roles, entityName);
|
|
284
|
+
const methodImplementation = this.generateMethodImplementation(action, entityName, handlers, moduleName, roles, moduleConfig);
|
|
285
|
+
const hasUserParam = roles.length > 0 && !roles.includes('all');
|
|
286
|
+
const variables = {
|
|
287
|
+
METHOD_NAME: action,
|
|
288
|
+
METHOD_PARAMS: methodParams,
|
|
289
|
+
USER_PARAM: hasUserParam ? ', user: AuthenticatedUser' : '',
|
|
290
|
+
RETURN_TYPE: returnType,
|
|
291
|
+
PERMISSION_CHECK: permissionCheck,
|
|
292
|
+
METHOD_IMPLEMENTATION: methodImplementation
|
|
293
|
+
};
|
|
294
|
+
return this.replaceTemplateVars(serviceTemplates_1.serviceTemplates.serviceMethod, variables);
|
|
295
|
+
}
|
|
296
|
+
generateService(moduleName, moduleConfig, hasGlobalPermissions) {
|
|
297
|
+
if (!moduleConfig.actions || !moduleConfig.models || moduleConfig.models.length === 0) {
|
|
298
|
+
return '';
|
|
299
|
+
}
|
|
300
|
+
const entityName = moduleConfig.models[0].name;
|
|
301
|
+
const entityLower = entityName.toLowerCase();
|
|
302
|
+
const actionPermissions = this.getActionPermissions(moduleName, moduleConfig);
|
|
303
|
+
const hasPermissions = !!(hasGlobalPermissions && moduleConfig.permissions && moduleConfig.permissions.length > 0);
|
|
304
|
+
const serviceMethods = Object.entries(moduleConfig.actions)
|
|
305
|
+
.map(([action, actionConfig]) => {
|
|
306
|
+
const roles = actionPermissions[action] || [];
|
|
307
|
+
const handlers = actionConfig.handlers || [];
|
|
308
|
+
return this.generateServiceMethod(action, entityName, roles, hasPermissions, handlers, moduleName, moduleConfig);
|
|
309
|
+
})
|
|
310
|
+
.join('\n\n');
|
|
311
|
+
const serviceClass = this.replaceTemplateVars(serviceTemplates_1.serviceTemplates.serviceClass, {
|
|
312
|
+
ENTITY_NAME: entityName,
|
|
313
|
+
ENTITY_LOWER: entityLower,
|
|
314
|
+
AUTH_SERVICE_PARAM: '',
|
|
315
|
+
SERVICE_METHODS: serviceMethods
|
|
316
|
+
});
|
|
317
|
+
const customImports = this.generateCustomImports(moduleConfig);
|
|
318
|
+
return this.replaceTemplateVars(serviceTemplates_1.serviceFileTemplate, {
|
|
319
|
+
ENTITY_NAME: entityName,
|
|
320
|
+
PERMISSIONS_IMPORT: hasPermissions ? "\nimport type { AuthenticatedUser } from '@currentjs/router';" : '',
|
|
321
|
+
CUSTOM_IMPORTS: customImports,
|
|
322
|
+
SERVICE_CLASS: serviceClass
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
generateCustomImports(moduleConfig) {
|
|
326
|
+
if (!moduleConfig.actions)
|
|
327
|
+
return '';
|
|
328
|
+
const imports = [];
|
|
329
|
+
Object.values(moduleConfig.actions).forEach(actionConfig => {
|
|
330
|
+
if (actionConfig.handlers) {
|
|
331
|
+
actionConfig.handlers.forEach(handler => {
|
|
332
|
+
if (handler.startsWith(constants_1.PATH_PATTERNS.MODULES_DIRECTIVE)) {
|
|
333
|
+
const functionName = this.extractFunctionName(handler);
|
|
334
|
+
const importPath = handler.replace(constants_1.PATH_PATTERNS.MODULES_DIRECTIVE, '../../');
|
|
335
|
+
imports.push(`import ${functionName} from '${importPath}';`);
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
return imports.length > 0 ? '\n' + imports.join('\n') : '';
|
|
341
|
+
}
|
|
342
|
+
generateFromYamlFile(yamlFilePath) {
|
|
343
|
+
const yamlContent = fs.readFileSync(yamlFilePath, 'utf8');
|
|
344
|
+
const config = (0, yaml_1.parse)(yamlContent);
|
|
345
|
+
const result = {};
|
|
346
|
+
const hasGlobalPermissions = this.hasPermissions(config);
|
|
347
|
+
if (config.modules) {
|
|
348
|
+
Object.entries(config.modules).forEach(([moduleName, moduleConfig]) => {
|
|
349
|
+
const serviceCode = this.generateService(moduleName, moduleConfig, hasGlobalPermissions);
|
|
350
|
+
if (serviceCode) {
|
|
351
|
+
result[moduleName] = serviceCode;
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
else {
|
|
356
|
+
const moduleName = 'Module';
|
|
357
|
+
const moduleConfig = config;
|
|
358
|
+
const serviceCode = this.generateService(moduleName, moduleConfig, hasGlobalPermissions);
|
|
359
|
+
if (serviceCode && moduleConfig.models && moduleConfig.models[0]) {
|
|
360
|
+
result[moduleConfig.models[0].name] = serviceCode;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
return result;
|
|
364
|
+
}
|
|
365
|
+
async generateAndSaveFiles(yamlFilePath = constants_1.COMMON_FILES.APP_YAML, outputDir = 'application', opts) {
|
|
366
|
+
const services = this.generateFromYamlFile(yamlFilePath);
|
|
367
|
+
const servicesDir = path.join(outputDir, 'services');
|
|
368
|
+
fs.mkdirSync(servicesDir, { recursive: true });
|
|
369
|
+
for (const [moduleName, serviceCode] of Object.entries(services)) {
|
|
370
|
+
const fileName = `${moduleName}Service.ts`;
|
|
371
|
+
const filePath = path.join(servicesDir, fileName);
|
|
372
|
+
// eslint-disable-next-line no-await-in-loop
|
|
373
|
+
await (0, generationRegistry_1.writeGeneratedFile)(filePath, serviceCode, { force: !!(opts === null || opts === void 0 ? void 0 : opts.force), skipOnConflict: !!(opts === null || opts === void 0 ? void 0 : opts.skipOnConflict) });
|
|
374
|
+
}
|
|
375
|
+
// eslint-disable-next-line no-console
|
|
376
|
+
console.log('\n' + colors_1.colors.green('Service files generated successfully!') + '\n');
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
exports.ServiceGenerator = ServiceGenerator;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
interface FieldConfig {
|
|
2
|
+
name: string;
|
|
3
|
+
type: string;
|
|
4
|
+
required?: boolean;
|
|
5
|
+
unique?: boolean;
|
|
6
|
+
auto?: boolean;
|
|
7
|
+
}
|
|
8
|
+
interface ModelConfig {
|
|
9
|
+
name: string;
|
|
10
|
+
fields: FieldConfig[];
|
|
11
|
+
}
|
|
12
|
+
export declare class StoreGenerator {
|
|
13
|
+
private typeMapping;
|
|
14
|
+
private mapType;
|
|
15
|
+
private generateRowFields;
|
|
16
|
+
private generateFilterableFields;
|
|
17
|
+
private generateFilterableFieldsArray;
|
|
18
|
+
private generateUpdatableFieldsArray;
|
|
19
|
+
private generateRowToModelMapping;
|
|
20
|
+
private generateModelToRowMapping;
|
|
21
|
+
private replaceTemplateVars;
|
|
22
|
+
generateStoreInterface(): string;
|
|
23
|
+
generateStore(modelConfig: ModelConfig): string;
|
|
24
|
+
generateStores(models: ModelConfig[]): Record<string, string>;
|
|
25
|
+
generateFromYamlFile(yamlFilePath: string): Record<string, string>;
|
|
26
|
+
generateAndSaveFiles(yamlFilePath?: string, outputDir?: string, opts?: {
|
|
27
|
+
force?: boolean;
|
|
28
|
+
skipOnConflict?: boolean;
|
|
29
|
+
}): Promise<void>;
|
|
30
|
+
}
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,191 @@
|
|
|
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.StoreGenerator = void 0;
|
|
37
|
+
const yaml_1 = require("yaml");
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
const storeTemplates_1 = require("./templates/storeTemplates");
|
|
41
|
+
const generationRegistry_1 = require("../utils/generationRegistry");
|
|
42
|
+
const colors_1 = require("../utils/colors");
|
|
43
|
+
const constants_1 = require("../utils/constants");
|
|
44
|
+
class StoreGenerator {
|
|
45
|
+
constructor() {
|
|
46
|
+
this.typeMapping = {
|
|
47
|
+
string: 'string',
|
|
48
|
+
number: 'number',
|
|
49
|
+
boolean: 'boolean',
|
|
50
|
+
datetime: 'Date',
|
|
51
|
+
json: 'any',
|
|
52
|
+
array: 'any[]',
|
|
53
|
+
object: 'object'
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
mapType(yamlType) {
|
|
57
|
+
return this.typeMapping[yamlType] || 'any';
|
|
58
|
+
}
|
|
59
|
+
generateRowFields(modelConfig) {
|
|
60
|
+
const fields = [];
|
|
61
|
+
modelConfig.fields.forEach(field => {
|
|
62
|
+
if (field.name === 'createdAt') {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const tsType = this.mapType(field.type);
|
|
66
|
+
const isOptional = !field.required && !field.auto;
|
|
67
|
+
const fieldDef = ` ${field.name}${isOptional ? '?' : ''}: ${tsType};`;
|
|
68
|
+
fields.push(fieldDef);
|
|
69
|
+
});
|
|
70
|
+
return fields.join('\n');
|
|
71
|
+
}
|
|
72
|
+
generateFilterableFields(modelConfig) {
|
|
73
|
+
const filterableFields = modelConfig.fields
|
|
74
|
+
.filter(field => ['string', 'number', 'boolean'].includes(field.type))
|
|
75
|
+
.map(field => `'${field.name}'`);
|
|
76
|
+
return filterableFields.join(' | ');
|
|
77
|
+
}
|
|
78
|
+
generateFilterableFieldsArray(modelConfig) {
|
|
79
|
+
const filterableFields = modelConfig.fields
|
|
80
|
+
.filter(field => ['string', 'number', 'boolean'].includes(field.type))
|
|
81
|
+
.map(field => `'${field.name}'`);
|
|
82
|
+
return filterableFields.join(', ');
|
|
83
|
+
}
|
|
84
|
+
generateUpdatableFieldsArray(modelConfig) {
|
|
85
|
+
const updatableFields = modelConfig.fields
|
|
86
|
+
.filter(field => field.name !== 'id' && field.name !== 'createdAt')
|
|
87
|
+
.map(field => `'${field.name}'`);
|
|
88
|
+
return updatableFields.join(', ');
|
|
89
|
+
}
|
|
90
|
+
generateRowToModelMapping(modelConfig) {
|
|
91
|
+
const mappings = modelConfig.fields.map(field => {
|
|
92
|
+
if (field.name === 'createdAt') {
|
|
93
|
+
return ' row.created_at';
|
|
94
|
+
}
|
|
95
|
+
return ` row.${field.name}`;
|
|
96
|
+
});
|
|
97
|
+
return mappings.join(',\n');
|
|
98
|
+
}
|
|
99
|
+
generateModelToRowMapping(modelConfig) {
|
|
100
|
+
const mappings = modelConfig.fields.map(field => {
|
|
101
|
+
if (field.name === 'createdAt') {
|
|
102
|
+
return ' created_at: model.createdAt';
|
|
103
|
+
}
|
|
104
|
+
return ` ${field.name}: model.${field.name}`;
|
|
105
|
+
});
|
|
106
|
+
return mappings.join(',\n');
|
|
107
|
+
}
|
|
108
|
+
replaceTemplateVars(template, variables) {
|
|
109
|
+
let result = template;
|
|
110
|
+
Object.entries(variables).forEach(([key, value]) => {
|
|
111
|
+
const regex = new RegExp(`{{${key}}}`, 'g');
|
|
112
|
+
result = result.replace(regex, value);
|
|
113
|
+
});
|
|
114
|
+
return result;
|
|
115
|
+
}
|
|
116
|
+
generateStoreInterface() {
|
|
117
|
+
return storeTemplates_1.fileTemplates.storeInterface;
|
|
118
|
+
}
|
|
119
|
+
generateStore(modelConfig) {
|
|
120
|
+
const entityName = modelConfig.name;
|
|
121
|
+
const tableName = entityName.toLowerCase() + 's';
|
|
122
|
+
const variables = {
|
|
123
|
+
ENTITY_NAME: entityName,
|
|
124
|
+
TABLE_NAME: tableName,
|
|
125
|
+
ROW_FIELDS: this.generateRowFields(modelConfig),
|
|
126
|
+
FILTERABLE_FIELDS: this.generateFilterableFields(modelConfig),
|
|
127
|
+
FILTERABLE_FIELDS_ARRAY: this.generateFilterableFieldsArray(modelConfig),
|
|
128
|
+
UPDATABLE_FIELDS_ARRAY: this.generateUpdatableFieldsArray(modelConfig),
|
|
129
|
+
ROW_TO_MODEL_MAPPING: this.generateRowToModelMapping(modelConfig),
|
|
130
|
+
MODEL_TO_ROW_MAPPING: this.generateModelToRowMapping(modelConfig)
|
|
131
|
+
};
|
|
132
|
+
const rowInterface = this.replaceTemplateVars(storeTemplates_1.storeTemplates.rowInterface, variables);
|
|
133
|
+
const conversionMethods = this.replaceTemplateVars(storeTemplates_1.storeTemplates.conversionMethods, variables);
|
|
134
|
+
const storeClass = this.replaceTemplateVars(storeTemplates_1.storeTemplates.storeClass, {
|
|
135
|
+
...variables,
|
|
136
|
+
CONVERSION_METHODS: conversionMethods
|
|
137
|
+
});
|
|
138
|
+
return this.replaceTemplateVars(storeTemplates_1.fileTemplates.storeFile, {
|
|
139
|
+
ENTITY_NAME: entityName,
|
|
140
|
+
ROW_INTERFACE: rowInterface,
|
|
141
|
+
STORE_CLASS: storeClass
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
generateStores(models) {
|
|
145
|
+
const result = {};
|
|
146
|
+
models.forEach(model => {
|
|
147
|
+
result[model.name] = this.generateStore(model);
|
|
148
|
+
});
|
|
149
|
+
return result;
|
|
150
|
+
}
|
|
151
|
+
generateFromYamlFile(yamlFilePath) {
|
|
152
|
+
const yamlContent = fs.readFileSync(yamlFilePath, 'utf8');
|
|
153
|
+
const config = (0, yaml_1.parse)(yamlContent);
|
|
154
|
+
const result = {};
|
|
155
|
+
if (config.modules) {
|
|
156
|
+
Object.values(config.modules).forEach(moduleConfig => {
|
|
157
|
+
if (moduleConfig.models && moduleConfig.models.length > 0) {
|
|
158
|
+
const stores = this.generateStores(moduleConfig.models);
|
|
159
|
+
Object.assign(result, stores);
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
else if (config.models) {
|
|
164
|
+
const module = config;
|
|
165
|
+
if (module.models && module.models.length > 0) {
|
|
166
|
+
const stores = this.generateStores(module.models);
|
|
167
|
+
Object.assign(result, stores);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return result;
|
|
171
|
+
}
|
|
172
|
+
async generateAndSaveFiles(yamlFilePath = constants_1.COMMON_FILES.APP_YAML, outputDir = 'infrastructure', opts) {
|
|
173
|
+
const stores = this.generateFromYamlFile(yamlFilePath);
|
|
174
|
+
const storesDir = path.join(outputDir, 'stores');
|
|
175
|
+
const interfacesDir = path.join(outputDir, 'interfaces');
|
|
176
|
+
fs.mkdirSync(storesDir, { recursive: true });
|
|
177
|
+
fs.mkdirSync(interfacesDir, { recursive: true });
|
|
178
|
+
const storeInterface = this.generateStoreInterface();
|
|
179
|
+
const interfaceFilePath = path.join(interfacesDir, constants_1.COMMON_FILES.STORE_INTERFACE);
|
|
180
|
+
await (0, generationRegistry_1.writeGeneratedFile)(interfaceFilePath, storeInterface, { force: !!(opts === null || opts === void 0 ? void 0 : opts.force), skipOnConflict: !!(opts === null || opts === void 0 ? void 0 : opts.skipOnConflict) });
|
|
181
|
+
for (const [entityName, storeCode] of Object.entries(stores)) {
|
|
182
|
+
const fileName = `${entityName}Store.ts`;
|
|
183
|
+
const filePath = path.join(storesDir, fileName);
|
|
184
|
+
// eslint-disable-next-line no-await-in-loop
|
|
185
|
+
await (0, generationRegistry_1.writeGeneratedFile)(filePath, storeCode, { force: !!(opts === null || opts === void 0 ? void 0 : opts.force), skipOnConflict: !!(opts === null || opts === void 0 ? void 0 : opts.skipOnConflict) });
|
|
186
|
+
}
|
|
187
|
+
// eslint-disable-next-line no-console
|
|
188
|
+
console.log('\n' + colors_1.colors.green('All store files generated successfully!') + '\n');
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
exports.StoreGenerator = StoreGenerator;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare class TemplateGenerator {
|
|
2
|
+
private generateForModule;
|
|
3
|
+
generateFromYamlFile(yamlFilePath: string): Record<string, {
|
|
4
|
+
file: string;
|
|
5
|
+
contents: string;
|
|
6
|
+
}>;
|
|
7
|
+
generateAndSaveFiles(yamlFilePath: string, _outputDir: string | undefined, opts?: {
|
|
8
|
+
force?: boolean;
|
|
9
|
+
skipOnConflict?: boolean;
|
|
10
|
+
}): Promise<void>;
|
|
11
|
+
}
|