@reldens/storage 0.54.0 → 0.56.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/lib/entities-generator.js +67 -690
- package/lib/entity-templates/prisma-model.template +0 -15
- package/lib/generators/base-generator.js +21 -0
- package/lib/generators/entities-config-generation.js +174 -0
- package/lib/generators/entities-generation.js +286 -0
- package/lib/generators/entities-translations-generation.js +135 -0
- package/lib/generators/models-generation.js +173 -0
- package/lib/mysql-tables-provider.js +1 -1
- package/lib/prisma/prisma-data-server.js +17 -6
- package/package.json +1 -1
|
@@ -9,7 +9,10 @@ const { Logger, sc } = require('@reldens/utils');
|
|
|
9
9
|
const { ObjectionJsDataServer } = require('./objection-js/objection-js-data-server');
|
|
10
10
|
const { MikroOrmDataServer } = require('./mikro-orm/mikro-orm-data-server');
|
|
11
11
|
const { PrismaDataServer } = require('./prisma/prisma-data-server');
|
|
12
|
-
const {
|
|
12
|
+
const { EntitiesGeneration } = require('./generators/entities-generation');
|
|
13
|
+
const { ModelsGeneration } = require('./generators/models-generation');
|
|
14
|
+
const { EntitiesConfigGeneration } = require('./generators/entities-config-generation');
|
|
15
|
+
const { EntitiesTranslationsGeneration } = require('./generators/entities-translations-generation');
|
|
13
16
|
|
|
14
17
|
class EntitiesGenerator
|
|
15
18
|
{
|
|
@@ -45,356 +48,31 @@ class EntitiesGenerator
|
|
|
45
48
|
this.server = sc.get(props, 'server', false);
|
|
46
49
|
this.connectionData = sc.get(props, 'connectionData', false);
|
|
47
50
|
this.isOverride = sc.get(props, 'isOverride', false);
|
|
51
|
+
this.prismaClient = sc.get(props, 'prismaClient', false);
|
|
48
52
|
this.generatedEntities = {};
|
|
49
53
|
this.existingEntities = {};
|
|
50
54
|
this.existingEntityFields = {};
|
|
51
55
|
this.existingModels = {};
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if(!entityTemplateContent){
|
|
63
|
-
Logger.critical('Failed to read entity template file: '+entityTemplatePath);
|
|
64
|
-
return false;
|
|
65
|
-
}
|
|
66
|
-
let entityClassName = sc.capitalizedCamelCase(tableName)+'Entity';
|
|
67
|
-
let titleProperty = this.determineTitleProperty(tableData.columns);
|
|
68
|
-
for(let columnName of Object.keys(tableData.columns)){
|
|
69
|
-
tableData.columns[columnName].tableName = tableName;
|
|
70
|
-
}
|
|
71
|
-
let propertiesConfig = this.generatePropertiesConfig(tableData.columns, titleProperty);
|
|
72
|
-
let titlePropertyDeclaration = titleProperty ? '\n let titleProperty = \''+titleProperty+'\';' : '';
|
|
73
|
-
let titlePropertyReturn = titleProperty ? '\n titleProperty,' : '';
|
|
74
|
-
let fieldsToRemoveFromEdit = ['id'];
|
|
75
|
-
if(tableData.columns['created_at']){
|
|
76
|
-
fieldsToRemoveFromEdit.push('created_at');
|
|
77
|
-
}
|
|
78
|
-
if(tableData.columns['updated_at']){
|
|
79
|
-
fieldsToRemoveFromEdit.push('updated_at');
|
|
80
|
-
}
|
|
81
|
-
let editPropertiesRemoval = this.getEditPropertiesRemoval(fieldsToRemoveFromEdit);
|
|
82
|
-
let listPropertiesDeclaration = this.getListPropertiesDeclaration(tableData.columns);
|
|
83
|
-
let showPropertiesDeclaration = this.getShowPropertiesDeclaration(tableData.columns);
|
|
84
|
-
let needsSc = listPropertiesDeclaration.includes('sc.removeFromArray')
|
|
85
|
-
|| editPropertiesRemoval.includes('sc.removeFromArray')
|
|
86
|
-
|| showPropertiesDeclaration.includes('sc.removeFromArray');
|
|
87
|
-
let scRequire = needsSc ? '\nconst { sc } = require(\'@reldens/utils\');' : '';
|
|
88
|
-
let replacements = {
|
|
89
|
-
entityClassName,
|
|
90
|
-
propertiesConfig,
|
|
91
|
-
titlePropertyDeclaration,
|
|
92
|
-
titlePropertyReturn,
|
|
93
|
-
showPropertiesDeclaration,
|
|
94
|
-
editPropertiesRemoval,
|
|
95
|
-
listPropertiesDeclaration,
|
|
96
|
-
scRequire
|
|
97
|
-
};
|
|
98
|
-
let entityContent = this.applyReplacements(entityTemplateContent, replacements);
|
|
99
|
-
let fileName = sc.kebabCase(tableName)+'-entity.js';
|
|
100
|
-
let filePath = FileHandler.joinPaths(this.entitiesFolder, fileName);
|
|
101
|
-
if(!FileHandler.writeFile(filePath, entityContent)){
|
|
102
|
-
Logger.critical('Failed to write entity file: '+filePath);
|
|
103
|
-
return false;
|
|
104
|
-
}
|
|
105
|
-
Logger.info('Generated entity file: '+fileName);
|
|
106
|
-
this.generatedEntities[tableName] = {
|
|
107
|
-
entityClassName,
|
|
108
|
-
entityFileName: fileName,
|
|
109
|
-
tableName,
|
|
110
|
-
titleProperty
|
|
111
|
-
};
|
|
112
|
-
return true;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
getFieldsToRemoveFromList(columns)
|
|
116
|
-
{
|
|
117
|
-
let fieldsToRemove = [];
|
|
118
|
-
for(let columnName of Object.keys(columns)){
|
|
119
|
-
let column = columns[columnName];
|
|
120
|
-
let type = column.type.toLowerCase();
|
|
121
|
-
if(['text', 'json', 'tinytext', 'mediumtext', 'longtext'].includes(type)){
|
|
122
|
-
fieldsToRemove.push(columnName);
|
|
123
|
-
}
|
|
124
|
-
if('password' === columnName.toLowerCase()){
|
|
125
|
-
fieldsToRemove.push(columnName);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
return fieldsToRemove;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
getEditPropertiesRemoval(fieldsToRemove)
|
|
132
|
-
{
|
|
133
|
-
if(1 === fieldsToRemove.length){
|
|
134
|
-
return 'let editProperties = [...propertiesKeys];\n editProperties.splice(editProperties.indexOf(\''+
|
|
135
|
-
fieldsToRemove[0]
|
|
136
|
-
+'\'), 1);';
|
|
137
|
-
}
|
|
138
|
-
return 'let editProperties = sc.removeFromArray([...propertiesKeys], [\''+fieldsToRemove.join('\', \'')+'\']);';
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
determineTitleProperty(columns)
|
|
142
|
-
{
|
|
143
|
-
if(columns['label']){
|
|
144
|
-
return 'label';
|
|
145
|
-
}
|
|
146
|
-
if(columns['title']){
|
|
147
|
-
return 'title';
|
|
148
|
-
}
|
|
149
|
-
if(columns['name']){
|
|
150
|
-
return 'name';
|
|
151
|
-
}
|
|
152
|
-
if(columns['key']){
|
|
153
|
-
return 'key';
|
|
154
|
-
}
|
|
155
|
-
return false;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
generatePropertiesConfig(columns, titleProperty)
|
|
159
|
-
{
|
|
160
|
-
let propertiesConfig = [];
|
|
161
|
-
for(let columnName of Object.keys(columns)){
|
|
162
|
-
let column = columns[columnName];
|
|
163
|
-
let propertyKey = titleProperty && titleProperty === columnName ? '[titleProperty]' : columnName;
|
|
164
|
-
if('id' === columnName){
|
|
165
|
-
propertiesConfig.push(columnName+': {}');
|
|
166
|
-
continue;
|
|
167
|
-
}
|
|
168
|
-
let props = this.getPropertyAttributes(column);
|
|
169
|
-
if(0 === props.length){
|
|
170
|
-
propertiesConfig.push(propertyKey+': {}');
|
|
171
|
-
continue;
|
|
172
|
-
}
|
|
173
|
-
propertiesConfig.push(
|
|
174
|
-
propertyKey+': {\n '+props.join(',\n ')+ '\n }'
|
|
175
|
-
);
|
|
176
|
-
}
|
|
177
|
-
return propertiesConfig.join(',\n ');
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
getPropertyAttributes(column)
|
|
181
|
-
{
|
|
182
|
-
let props = [];
|
|
183
|
-
let type = column.type.toLowerCase();
|
|
184
|
-
this.addTypeAttribute(props, column, type);
|
|
185
|
-
this.addRequiredAttribute(props, column);
|
|
186
|
-
return props;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
addTypeAttribute(props, column, type)
|
|
190
|
-
{
|
|
191
|
-
if(column.referencedTable){
|
|
192
|
-
props.push('type: \'reference\'');
|
|
193
|
-
props.push('reference: \''+this.getReferenceTable(column)+'\'');
|
|
194
|
-
return;
|
|
195
|
-
}
|
|
196
|
-
if('date' === type || 'datetime' === type || 'timestamp' === type){
|
|
197
|
-
props.push('type: \'datetime\'');
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
|
-
if('text' === type || 'longtext' === type || 'mediumtext' === type || 'tinytext' === type || 'json' === type){
|
|
201
|
-
props.push('type: \'textarea\'');
|
|
202
|
-
return;
|
|
203
|
-
}
|
|
204
|
-
if('tinyint' === type){
|
|
205
|
-
props.push('type: \'boolean\'');
|
|
206
|
-
return;
|
|
207
|
-
}
|
|
208
|
-
if('enum' === type){
|
|
209
|
-
this.addEnumValues(props, column);
|
|
210
|
-
return;
|
|
211
|
-
}
|
|
212
|
-
if(type.includes('int')){
|
|
213
|
-
props.push('type: \'number\'');
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
if('varchar' === type || 'char' === type){
|
|
217
|
-
return;
|
|
218
|
-
}
|
|
219
|
-
let jsType = TypeMapper.mapDbTypeToJsType(type);
|
|
220
|
-
if(jsType && 'string' !== jsType){
|
|
221
|
-
props.push('type: \''+jsType+'\'');
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
addEnumValues(props, column)
|
|
226
|
-
{
|
|
227
|
-
let enumValues = this.parseEnumValues(column.columnType);
|
|
228
|
-
if(0 === enumValues.length){
|
|
229
|
-
return;
|
|
230
|
-
}
|
|
231
|
-
let availableValues = [];
|
|
232
|
-
for(let i = 0; i < enumValues.length; i++){
|
|
233
|
-
availableValues.push('{value: '+(i+1)+', label: \''+enumValues[i]+'\'}');
|
|
234
|
-
}
|
|
235
|
-
props.push(
|
|
236
|
-
'availableValues: [\n '+
|
|
237
|
-
availableValues.join(',\n ')+
|
|
238
|
-
'\n ]'
|
|
239
|
-
);
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
parseEnumValues(columnType)
|
|
243
|
-
{
|
|
244
|
-
if(!columnType){
|
|
245
|
-
return [];
|
|
246
|
-
}
|
|
247
|
-
let match = columnType.match(/^enum\('(.+)'\)$/i);
|
|
248
|
-
if(!match){
|
|
249
|
-
return [];
|
|
250
|
-
}
|
|
251
|
-
let valuesString = match[1];
|
|
252
|
-
return valuesString.split('\',\'');
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
addRequiredAttribute(props, column)
|
|
256
|
-
{
|
|
257
|
-
if(!column.nullable && !column.default){
|
|
258
|
-
props.push('isRequired: true');
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
getReferenceTable(column)
|
|
263
|
-
{
|
|
264
|
-
if(column.referencedTable){
|
|
265
|
-
return column.referencedTable;
|
|
266
|
-
}
|
|
267
|
-
let refTableName = column.name.replace('_id', '');
|
|
268
|
-
if(refTableName.includes('_')){
|
|
269
|
-
let parts = refTableName.split('_');
|
|
270
|
-
if(column.tableName && column.tableName === parts[0]){
|
|
271
|
-
refTableName = parts.slice(1).join('_');
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
return refTableName;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
getShowPropertiesDeclaration(columns)
|
|
278
|
-
{
|
|
279
|
-
let passwordFields = this.getPasswordFields(columns);
|
|
280
|
-
if(0 === passwordFields.length){
|
|
281
|
-
return 'let showProperties = propertiesKeys;';
|
|
282
|
-
}
|
|
283
|
-
if(1 === passwordFields.length){
|
|
284
|
-
return 'let showProperties = [...propertiesKeys];\n showProperties.splice(showProperties.indexOf(\''+
|
|
285
|
-
passwordFields[0]+
|
|
286
|
-
'\'), 1);';
|
|
287
|
-
}
|
|
288
|
-
return 'let showProperties = sc.removeFromArray([...propertiesKeys], [\''+passwordFields.join('\', \'')+'\']);';
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
getPasswordFields(columns)
|
|
292
|
-
{
|
|
293
|
-
let passwordFields = [];
|
|
294
|
-
for(let columnName of Object.keys(columns)){
|
|
295
|
-
if('password' === columnName.toLowerCase()){
|
|
296
|
-
passwordFields.push(columnName);
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
return passwordFields;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
getListPropertiesDeclaration(columns)
|
|
303
|
-
{
|
|
304
|
-
let fieldsToRemove = this.getFieldsToRemoveFromList(columns);
|
|
305
|
-
if(0 === fieldsToRemove.length){
|
|
306
|
-
return 'let listProperties = propertiesKeys;';
|
|
307
|
-
}
|
|
308
|
-
if(1 === fieldsToRemove.length){
|
|
309
|
-
return 'let listProperties = [...propertiesKeys];\n listProperties.splice(listProperties.indexOf(\''+
|
|
310
|
-
fieldsToRemove[0]+
|
|
311
|
-
'\'), 1);';
|
|
312
|
-
}
|
|
313
|
-
return 'let listProperties = sc.removeFromArray([...propertiesKeys], [\''+fieldsToRemove.join('\', \'')+'\']);';
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
async generateModelFile(tableName, tableData, driverKey)
|
|
317
|
-
{
|
|
318
|
-
let modelTemplatePath = this.templates[driverKey];
|
|
319
|
-
if(!FileHandler.exists(modelTemplatePath)){
|
|
320
|
-
Logger.critical('Model template file "'+modelTemplatePath+'" not found for driver "'+driverKey+'".');
|
|
321
|
-
return false;
|
|
322
|
-
}
|
|
323
|
-
let modelTemplateContent = FileHandler.fetchFileContents(modelTemplatePath);
|
|
324
|
-
if(!modelTemplateContent){
|
|
325
|
-
Logger.critical('Failed to read model template file: '+modelTemplatePath);
|
|
326
|
-
return false;
|
|
327
|
-
}
|
|
328
|
-
let modelClassName = sc.capitalizedCamelCase(tableName)+'Model';
|
|
329
|
-
let modelPropertiesList = Object.keys(tableData.columns).join(', ');
|
|
330
|
-
let modelPropertiesConstructor = Object.keys(tableData.columns)
|
|
331
|
-
.map(columnName => 'this.'+columnName+' = '+columnName+';')
|
|
332
|
-
.join('\n ');
|
|
333
|
-
let modelRelations = '';
|
|
334
|
-
let entityPropertiesDefinition = this.getEntityPropertiesDefinition(tableData.columns, driverKey);
|
|
335
|
-
let replacements = {
|
|
336
|
-
modelClassName,
|
|
337
|
-
tableName: 'prisma' === driverKey ? tableName.toLowerCase() : tableName,
|
|
338
|
-
modelPropertiesList,
|
|
339
|
-
modelPropertiesConstructor,
|
|
340
|
-
modelRelations,
|
|
341
|
-
entityPropertiesDefinition
|
|
342
|
-
};
|
|
343
|
-
let modelContent = this.applyReplacements(modelTemplateContent, replacements);
|
|
344
|
-
let fileName = sc.kebabCase(tableName)+'-model.js';
|
|
345
|
-
let driverFolder = FileHandler.joinPaths(this.modelsFolder, driverKey);
|
|
346
|
-
FileHandler.createFolder(driverFolder);
|
|
347
|
-
let filePath = FileHandler.joinPaths(driverFolder, fileName);
|
|
348
|
-
if(!FileHandler.writeFile(filePath, modelContent)){
|
|
349
|
-
Logger.critical('Failed to write model file: '+fileName);
|
|
350
|
-
return false;
|
|
351
|
-
}
|
|
352
|
-
Logger.info('Generated model file: '+fileName);
|
|
353
|
-
this.generatedEntities[tableName].modelClassName = modelClassName;
|
|
354
|
-
this.generatedEntities[tableName].modelFileName = fileName;
|
|
355
|
-
this.generatedEntities[tableName].driverKey = driverKey;
|
|
356
|
-
return true;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
getEntityPropertiesDefinition(columns, driverKey)
|
|
360
|
-
{
|
|
361
|
-
if('mikro-orm' === driverKey){
|
|
362
|
-
let entityProps = [];
|
|
363
|
-
for(let columnName of Object.keys(columns)){
|
|
364
|
-
let column = columns[columnName];
|
|
365
|
-
let isPrimary = 'PRI' === column.key;
|
|
366
|
-
let type = TypeMapper.mapDbTypeToJsType(column.type);
|
|
367
|
-
let propDef = columnName+': { type: \''+type+'\'';
|
|
368
|
-
if(isPrimary){
|
|
369
|
-
propDef += ', primary: true';
|
|
370
|
-
}
|
|
371
|
-
if(column.nullable){
|
|
372
|
-
propDef += ', nullable: true';
|
|
373
|
-
}
|
|
374
|
-
propDef += ' }';
|
|
375
|
-
entityProps.push(propDef);
|
|
376
|
-
}
|
|
377
|
-
return entityProps.join(',\n ');
|
|
378
|
-
}
|
|
379
|
-
if('prisma' === driverKey){
|
|
380
|
-
let prismaProps = [];
|
|
381
|
-
for(let columnName of Object.keys(columns)){
|
|
382
|
-
let column = columns[columnName];
|
|
383
|
-
let prismaType = TypeMapper.mapDbTypeToPrismaType(column.type);
|
|
384
|
-
let isPrimary = 'PRI' === column.key;
|
|
385
|
-
let propDef = columnName+': {\n type: \''+prismaType+'\'';
|
|
386
|
-
if(isPrimary){
|
|
387
|
-
propDef += ',\n id: true';
|
|
388
|
-
}
|
|
389
|
-
if(column.nullable){
|
|
390
|
-
propDef += ',\n optional: true';
|
|
391
|
-
}
|
|
392
|
-
propDef += '\n }';
|
|
393
|
-
prismaProps.push(propDef);
|
|
56
|
+
this.entitiesGeneration = new EntitiesGeneration({
|
|
57
|
+
entitiesFolder: this.entitiesFolder,
|
|
58
|
+
templatePath: this.templates['entity']
|
|
59
|
+
});
|
|
60
|
+
this.modelsGeneration = new ModelsGeneration({
|
|
61
|
+
modelsFolder: this.modelsFolder,
|
|
62
|
+
templates: {
|
|
63
|
+
'objection-js': this.templates['objection-js'],
|
|
64
|
+
'mikro-orm': this.templates['mikro-orm'],
|
|
65
|
+
'prisma': this.templates['prisma']
|
|
394
66
|
}
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
67
|
+
});
|
|
68
|
+
this.entitiesConfigGeneration = new EntitiesConfigGeneration({
|
|
69
|
+
entitiesConfigPath: this.entitiesConfigPath,
|
|
70
|
+
templatePath: this.templates['entities-config']
|
|
71
|
+
});
|
|
72
|
+
this.entitiesTranslationsGeneration = new EntitiesTranslationsGeneration({
|
|
73
|
+
entitiesTranslationsPath: this.entitiesTranslationsPath,
|
|
74
|
+
templatePath: this.templates['entities-translations']
|
|
75
|
+
});
|
|
398
76
|
}
|
|
399
77
|
|
|
400
78
|
detectExistingEntities()
|
|
@@ -411,7 +89,6 @@ class EntitiesGenerator
|
|
|
411
89
|
this.existingEntities[tableName] = {entityFileName: file, tableName};
|
|
412
90
|
this.detectExistingEntityFields(tableName, file);
|
|
413
91
|
}
|
|
414
|
-
this.detectExistingModels();
|
|
415
92
|
Logger.info('Detected '+Object.keys(this.existingEntities).length+' existing entities.');
|
|
416
93
|
}
|
|
417
94
|
|
|
@@ -438,6 +115,7 @@ class EntitiesGenerator
|
|
|
438
115
|
};
|
|
439
116
|
}
|
|
440
117
|
}
|
|
118
|
+
Logger.info('Detected '+Object.keys(this.existingModels).length+' existing models.');
|
|
441
119
|
}
|
|
442
120
|
|
|
443
121
|
detectExistingEntityFields(tableName, fileName)
|
|
@@ -552,11 +230,7 @@ class EntitiesGenerator
|
|
|
552
230
|
|
|
553
231
|
entityExistsInConfig(tableName)
|
|
554
232
|
{
|
|
555
|
-
|
|
556
|
-
return false;
|
|
557
|
-
}
|
|
558
|
-
let configContent = FileHandler.readFile(this.entitiesConfigPath);
|
|
559
|
-
return configContent && configContent.includes(sc.camelCase(tableName)+':');
|
|
233
|
+
return this.entitiesConfigGeneration.entityExistsInConfig(tableName, FileHandler.readFile(this.entitiesConfigPath));
|
|
560
234
|
}
|
|
561
235
|
|
|
562
236
|
modelExistsInRegistered(tableName, driverKey)
|
|
@@ -569,337 +243,6 @@ class EntitiesGenerator
|
|
|
569
243
|
return registeredContent && registeredContent.includes(sc.camelCase(tableName)+':');
|
|
570
244
|
}
|
|
571
245
|
|
|
572
|
-
generateEntitiesConfigFile()
|
|
573
|
-
{
|
|
574
|
-
if(this.isOverride){
|
|
575
|
-
return this.regenerateEntitiesConfigFile();
|
|
576
|
-
}
|
|
577
|
-
return this.appendToEntitiesConfigFile();
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
regenerateEntitiesConfigFile()
|
|
581
|
-
{
|
|
582
|
-
let configTemplateContent = FileHandler.fetchFileContents(this.templates['entities-config']);
|
|
583
|
-
if(!configTemplateContent){
|
|
584
|
-
Logger.critical('Failed to read entities config template file: '+this.templates['entities-config']);
|
|
585
|
-
return false;
|
|
586
|
-
}
|
|
587
|
-
let allEntities = Object.assign({}, this.existingEntities, this.generatedEntities);
|
|
588
|
-
let replacements = {
|
|
589
|
-
requireStatements: this.getRequireStatements(allEntities),
|
|
590
|
-
entitiesConfigExport: this.getEntitiesConfigExport(allEntities)
|
|
591
|
-
};
|
|
592
|
-
if(!FileHandler.writeFile(
|
|
593
|
-
this.entitiesConfigPath,
|
|
594
|
-
this.applyReplacements(configTemplateContent, replacements)
|
|
595
|
-
)){
|
|
596
|
-
Logger.critical('Failed to write entities config file: '+this.entitiesConfigPath);
|
|
597
|
-
return false;
|
|
598
|
-
}
|
|
599
|
-
Logger.info('Regenerated entities config file: '+this.entitiesConfigPath);
|
|
600
|
-
return true;
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
appendToEntitiesConfigFile()
|
|
604
|
-
{
|
|
605
|
-
if(0 === Object.keys(this.generatedEntities).length){
|
|
606
|
-
return true;
|
|
607
|
-
}
|
|
608
|
-
if(!FileHandler.exists(this.entitiesConfigPath)){
|
|
609
|
-
Logger.info('Entities config file does not exist, creating new file.');
|
|
610
|
-
return this.regenerateEntitiesConfigFile();
|
|
611
|
-
}
|
|
612
|
-
let existingContent = FileHandler.readFile(this.entitiesConfigPath);
|
|
613
|
-
if(!existingContent){
|
|
614
|
-
Logger.error('Could not read existing entities config file.');
|
|
615
|
-
return false;
|
|
616
|
-
}
|
|
617
|
-
let newRequires = [];
|
|
618
|
-
let newConfigs = [];
|
|
619
|
-
for(let tableName of Object.keys(this.generatedEntities)){
|
|
620
|
-
let entity = this.generatedEntities[tableName];
|
|
621
|
-
let entityClassName = sc.get(entity, 'entityClassName', sc.capitalizedCamelCase(tableName)+'Entity');
|
|
622
|
-
let entityFileName = sc.get(entity, 'entityFileName', sc.kebabCase(tableName)+'-entity').replace('.js', '');
|
|
623
|
-
let requireStatement = 'const { '+ entityClassName+' } = require(\'./entities/'+entityFileName+'\');';
|
|
624
|
-
let configEntry = sc.camelCase(tableName)+': '+entityClassName+'.propertiesConfig()';
|
|
625
|
-
if(!existingContent.includes(requireStatement)){
|
|
626
|
-
newRequires.push(requireStatement);
|
|
627
|
-
}
|
|
628
|
-
if(!this.entityExistsInConfig(tableName)){
|
|
629
|
-
newConfigs.push(configEntry);
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
if(0 === newRequires.length && 0 === newConfigs.length){
|
|
633
|
-
return true;
|
|
634
|
-
}
|
|
635
|
-
let normalizedContent = existingContent.replace(/\s+/g, ' ');
|
|
636
|
-
let entitiesConfigPattern = /let\s+entitiesConfig\s*=\s*\{/;
|
|
637
|
-
let entitiesConfigMatch = normalizedContent.match(entitiesConfigPattern);
|
|
638
|
-
if(!entitiesConfigMatch){
|
|
639
|
-
Logger.error('Could not find entitiesConfig variable in config file.');
|
|
640
|
-
return false;
|
|
641
|
-
}
|
|
642
|
-
let entitiesConfigStart = existingContent.indexOf(entitiesConfigMatch[0]);
|
|
643
|
-
let searchStart = entitiesConfigStart + entitiesConfigMatch[0].length;
|
|
644
|
-
let braceCount = 1;
|
|
645
|
-
let entitiesConfigEnd = -1;
|
|
646
|
-
for(let i = searchStart; i < existingContent.length; i++){
|
|
647
|
-
if('{' === existingContent[i]){
|
|
648
|
-
braceCount++;
|
|
649
|
-
}
|
|
650
|
-
if('}' === existingContent[i]){
|
|
651
|
-
braceCount--;
|
|
652
|
-
if(0 === braceCount){
|
|
653
|
-
entitiesConfigEnd = i;
|
|
654
|
-
break;
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
if(-1 === entitiesConfigEnd){
|
|
659
|
-
Logger.error('Could not find end of entitiesConfig object.');
|
|
660
|
-
return false;
|
|
661
|
-
}
|
|
662
|
-
let firstRequirePosition = existingContent.indexOf('const {');
|
|
663
|
-
if(-1 === firstRequirePosition){
|
|
664
|
-
Logger.error('Could not find require statements in config file.');
|
|
665
|
-
return false;
|
|
666
|
-
}
|
|
667
|
-
let beforeFirstRequire = existingContent.substring(0, firstRequirePosition);
|
|
668
|
-
let afterFirstRequire = existingContent.substring(firstRequirePosition, entitiesConfigEnd);
|
|
669
|
-
let afterConfig = existingContent.substring(entitiesConfigEnd);
|
|
670
|
-
let updatedContent = beforeFirstRequire;
|
|
671
|
-
if(0 < newRequires.length){
|
|
672
|
-
updatedContent += newRequires.join('\n') + '\n';
|
|
673
|
-
}
|
|
674
|
-
updatedContent += afterFirstRequire;
|
|
675
|
-
if(0 < newConfigs.length){
|
|
676
|
-
let trimmedContent = afterFirstRequire.trimEnd();
|
|
677
|
-
if(!trimmedContent.endsWith(',')){
|
|
678
|
-
updatedContent = updatedContent.trimEnd() + ',';
|
|
679
|
-
}
|
|
680
|
-
updatedContent += '\n ' + newConfigs.join(',\n ');
|
|
681
|
-
}
|
|
682
|
-
updatedContent += afterConfig;
|
|
683
|
-
if(!FileHandler.writeFile(this.entitiesConfigPath, updatedContent)){
|
|
684
|
-
Logger.error('Failed to append to entities config file: '+this.entitiesConfigPath);
|
|
685
|
-
return false;
|
|
686
|
-
}
|
|
687
|
-
Logger.info('Updated entities config file with '+newConfigs.length+' new entities.');
|
|
688
|
-
return true;
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
getRequireStatements(allEntities)
|
|
692
|
-
{
|
|
693
|
-
let requireStatements = [];
|
|
694
|
-
for(let tableName of Object.keys(allEntities)){
|
|
695
|
-
let entity = allEntities[tableName];
|
|
696
|
-
let entityClassName = sc.get(entity, 'entityClassName', sc.capitalizedCamelCase(tableName)+'Entity');
|
|
697
|
-
let entityFileName = sc.get(entity, 'entityFileName', sc.kebabCase(tableName)+'-entity').replace('.js', '');
|
|
698
|
-
requireStatements.push(
|
|
699
|
-
'const { '+ entityClassName+' } = require(\'./entities/'+entityFileName+'\');'
|
|
700
|
-
);
|
|
701
|
-
}
|
|
702
|
-
return requireStatements.join('\n');
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
getEntitiesConfigExport(allEntities)
|
|
706
|
-
{
|
|
707
|
-
let entitiesConfigExport = [];
|
|
708
|
-
for(let tableName of Object.keys(allEntities)){
|
|
709
|
-
entitiesConfigExport.push(
|
|
710
|
-
sc.camelCase(tableName)+': '
|
|
711
|
-
+sc.get(allEntities[tableName], 'entityClassName', sc.capitalizedCamelCase(tableName)+'Entity')
|
|
712
|
-
+'.propertiesConfig()'
|
|
713
|
-
);
|
|
714
|
-
}
|
|
715
|
-
return entitiesConfigExport.join(',\n ');
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
generateEntitiesTranslationsFile()
|
|
719
|
-
{
|
|
720
|
-
if(this.isOverride){
|
|
721
|
-
return this.regenerateEntitiesTranslationsFile();
|
|
722
|
-
}
|
|
723
|
-
return this.appendToEntitiesTranslationsFile();
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
regenerateEntitiesTranslationsFile()
|
|
727
|
-
{
|
|
728
|
-
let translationsTemplatePath = this.templates['entities-translations'];
|
|
729
|
-
if(!FileHandler.exists(translationsTemplatePath)){
|
|
730
|
-
Logger.critical('Entities translations template file not found: '+translationsTemplatePath);
|
|
731
|
-
return false;
|
|
732
|
-
}
|
|
733
|
-
let translationsTemplateContent = FileHandler.fetchFileContents(translationsTemplatePath);
|
|
734
|
-
if(!translationsTemplateContent){
|
|
735
|
-
Logger.critical('Failed to read entities translations template file: '+translationsTemplatePath);
|
|
736
|
-
return false;
|
|
737
|
-
}
|
|
738
|
-
let translationsContent = translationsTemplateContent.replace(
|
|
739
|
-
/{{labels}}/g,
|
|
740
|
-
this.getTranslationLabels(Object.assign({}, this.existingEntities, this.generatedEntities))
|
|
741
|
-
);
|
|
742
|
-
if(!FileHandler.writeFile(this.entitiesTranslationsPath, translationsContent)){
|
|
743
|
-
Logger.critical('Failed to write entities translations file: '+this.entitiesTranslationsPath);
|
|
744
|
-
return false;
|
|
745
|
-
}
|
|
746
|
-
Logger.info('Regenerated entities translations file: '+this.entitiesTranslationsPath);
|
|
747
|
-
return true;
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
appendToEntitiesTranslationsFile()
|
|
751
|
-
{
|
|
752
|
-
if(0 === Object.keys(this.generatedEntities).length){
|
|
753
|
-
return true;
|
|
754
|
-
}
|
|
755
|
-
if(!FileHandler.exists(this.entitiesTranslationsPath)){
|
|
756
|
-
Logger.info('Entities translations file does not exist, creating new file.');
|
|
757
|
-
return this.regenerateEntitiesTranslationsFile();
|
|
758
|
-
}
|
|
759
|
-
let existingContent = FileHandler.readFile(this.entitiesTranslationsPath);
|
|
760
|
-
if(!existingContent){
|
|
761
|
-
Logger.error('Could not read existing translations file.');
|
|
762
|
-
return false;
|
|
763
|
-
}
|
|
764
|
-
let newTranslations = [];
|
|
765
|
-
for(let tableName of Object.keys(this.generatedEntities)){
|
|
766
|
-
if(!existingContent.includes('\''+tableName+'\':')){
|
|
767
|
-
newTranslations.push(
|
|
768
|
-
'\''+tableName+'\': \''
|
|
769
|
-
+tableName.split('_').map(word => word.charAt(0).toUpperCase()+word.slice(1)).join(' ')+'\''
|
|
770
|
-
);
|
|
771
|
-
}
|
|
772
|
-
}
|
|
773
|
-
if(0 === newTranslations.length){
|
|
774
|
-
return true;
|
|
775
|
-
}
|
|
776
|
-
let normalizedContent = existingContent.replace(/\s+/g, ' ');
|
|
777
|
-
let labelsPattern = /labels\s*:\s*\{/;
|
|
778
|
-
let labelsMatch = normalizedContent.match(labelsPattern);
|
|
779
|
-
if(!labelsMatch){
|
|
780
|
-
Logger.error('Could not find labels object in translations file.');
|
|
781
|
-
return false;
|
|
782
|
-
}
|
|
783
|
-
let labelsStart = existingContent.indexOf(labelsMatch[0]);
|
|
784
|
-
let searchStart = labelsStart + labelsMatch[0].length;
|
|
785
|
-
let braceCount = 1;
|
|
786
|
-
let labelsEnd = -1;
|
|
787
|
-
for(let i = searchStart; i < existingContent.length; i++){
|
|
788
|
-
if('{' === existingContent[i]){
|
|
789
|
-
braceCount++;
|
|
790
|
-
}
|
|
791
|
-
if('}' === existingContent[i]){
|
|
792
|
-
braceCount--;
|
|
793
|
-
if(0 === braceCount){
|
|
794
|
-
labelsEnd = i;
|
|
795
|
-
break;
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
}
|
|
799
|
-
if(-1 === labelsEnd){
|
|
800
|
-
Logger.error('Could not find end of labels object.');
|
|
801
|
-
return false;
|
|
802
|
-
}
|
|
803
|
-
let beforeLabelsEnd = existingContent.substring(0, labelsEnd);
|
|
804
|
-
let contentBeforeEnd = beforeLabelsEnd.trimEnd();
|
|
805
|
-
let lastChar = contentBeforeEnd[contentBeforeEnd.length - 1];
|
|
806
|
-
if('{' !== lastChar && ',' !== lastChar){
|
|
807
|
-
beforeLabelsEnd = contentBeforeEnd + ',';
|
|
808
|
-
}
|
|
809
|
-
let updatedContent = beforeLabelsEnd + '\n ' + newTranslations.join(',\n ') + existingContent.substring(labelsEnd);
|
|
810
|
-
if(!FileHandler.writeFile(this.entitiesTranslationsPath, updatedContent)){
|
|
811
|
-
Logger.error('Failed to append to entities translations file: '+this.entitiesTranslationsPath);
|
|
812
|
-
return false;
|
|
813
|
-
}
|
|
814
|
-
Logger.info('Updated translations file with '+newTranslations.length+' new labels.');
|
|
815
|
-
return true;
|
|
816
|
-
}
|
|
817
|
-
|
|
818
|
-
getTranslationLabels(allEntities)
|
|
819
|
-
{
|
|
820
|
-
let labels = [];
|
|
821
|
-
for(let tableName of Object.keys(allEntities)){
|
|
822
|
-
labels.push(
|
|
823
|
-
'\''+tableName+'\': \''
|
|
824
|
-
+tableName.split('_').map(word => word.charAt(0).toUpperCase()+word.slice(1)).join(' ')
|
|
825
|
-
+'\''
|
|
826
|
-
);
|
|
827
|
-
}
|
|
828
|
-
return labels.join(',\n ');
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
generateRegisteredModelsFile(driverKey)
|
|
832
|
-
{
|
|
833
|
-
let registeredTemplatePath = this.templates['registered-models'];
|
|
834
|
-
if(!FileHandler.exists(registeredTemplatePath)){
|
|
835
|
-
Logger.critical('Registered models template file not found: '+registeredTemplatePath);
|
|
836
|
-
return false;
|
|
837
|
-
}
|
|
838
|
-
let registeredTemplateContent = FileHandler.fetchFileContents(registeredTemplatePath);
|
|
839
|
-
if(!registeredTemplateContent){
|
|
840
|
-
Logger.critical('Failed to read registered models template file: '+registeredTemplatePath);
|
|
841
|
-
return false;
|
|
842
|
-
}
|
|
843
|
-
let allEntities = Object.assign({}, this.existingEntities, this.generatedEntities);
|
|
844
|
-
let replacements = {
|
|
845
|
-
registeredModels: this.getRegisteredModels(allEntities, driverKey),
|
|
846
|
-
registeredEntitiesObject: this.getRegisteredEntitiesObject(allEntities, driverKey)
|
|
847
|
-
};
|
|
848
|
-
let registeredContent = this.applyReplacements(registeredTemplateContent, replacements);
|
|
849
|
-
let driverFolder = FileHandler.joinPaths(this.modelsFolder, driverKey);
|
|
850
|
-
let filePath = FileHandler.joinPaths(driverFolder, 'registered-models-'+driverKey+'.js');
|
|
851
|
-
if(!FileHandler.writeFile(filePath, registeredContent)){
|
|
852
|
-
Logger.critical('Failed to write registered models file: '+filePath);
|
|
853
|
-
return false;
|
|
854
|
-
}
|
|
855
|
-
Logger.info('Generated registered models file: '+filePath);
|
|
856
|
-
return true;
|
|
857
|
-
}
|
|
858
|
-
|
|
859
|
-
getRegisteredModels(allEntities, driverKey)
|
|
860
|
-
{
|
|
861
|
-
let registeredModels = [];
|
|
862
|
-
for(let tableName of Object.keys(allEntities)){
|
|
863
|
-
let entity = allEntities[tableName];
|
|
864
|
-
if(entity.driverKey && driverKey !== entity.driverKey){
|
|
865
|
-
if(!this.existingModels[tableName] || !this.existingModels[tableName][driverKey]){
|
|
866
|
-
continue;
|
|
867
|
-
}
|
|
868
|
-
}
|
|
869
|
-
let modelClassName = entity.modelClassName || sc.capitalizedCamelCase(tableName)+'Model';
|
|
870
|
-
let modelFileName = entity.modelFileName || sc.kebabCase(tableName)+'-model.js';
|
|
871
|
-
registeredModels.push(
|
|
872
|
-
'const { '+modelClassName+' } = require(\'./'+modelFileName.replace('.js', '')+'\');'
|
|
873
|
-
);
|
|
874
|
-
}
|
|
875
|
-
return registeredModels.join('\n');
|
|
876
|
-
}
|
|
877
|
-
|
|
878
|
-
getRegisteredEntitiesObject(allEntities, driverKey)
|
|
879
|
-
{
|
|
880
|
-
let registeredEntitiesObject = [];
|
|
881
|
-
for(let tableName of Object.keys(allEntities)){
|
|
882
|
-
let entity = allEntities[tableName];
|
|
883
|
-
if(entity.driverKey && driverKey !== entity.driverKey){
|
|
884
|
-
if(!this.existingModels[tableName] || !this.existingModels[tableName][driverKey]){
|
|
885
|
-
continue;
|
|
886
|
-
}
|
|
887
|
-
}
|
|
888
|
-
let modelClassName = entity.modelClassName || sc.capitalizedCamelCase(tableName)+'Model';
|
|
889
|
-
registeredEntitiesObject.push(sc.camelCase(tableName)+': '+modelClassName);
|
|
890
|
-
}
|
|
891
|
-
return registeredEntitiesObject.join(',\n ');
|
|
892
|
-
}
|
|
893
|
-
|
|
894
|
-
applyReplacements(content, replacements)
|
|
895
|
-
{
|
|
896
|
-
let result = content;
|
|
897
|
-
for(let placeholder of Object.keys(replacements)){
|
|
898
|
-
result = result.replace(new RegExp('{{'+placeholder+'}}','g'), replacements[placeholder]);
|
|
899
|
-
}
|
|
900
|
-
return result;
|
|
901
|
-
}
|
|
902
|
-
|
|
903
246
|
async generate()
|
|
904
247
|
{
|
|
905
248
|
if(!this.server){
|
|
@@ -920,6 +263,7 @@ class EntitiesGenerator
|
|
|
920
263
|
return false;
|
|
921
264
|
}
|
|
922
265
|
this.detectExistingEntities();
|
|
266
|
+
this.detectExistingModels();
|
|
923
267
|
let driverKey = sc.get(
|
|
924
268
|
this.connectionData,
|
|
925
269
|
'driver',
|
|
@@ -935,12 +279,41 @@ class EntitiesGenerator
|
|
|
935
279
|
}
|
|
936
280
|
for(let tableName of Object.keys(tablesToGenerate)){
|
|
937
281
|
let tableData = tablesToGenerate[tableName];
|
|
938
|
-
await this.generateEntityFile(tableName, tableData);
|
|
939
|
-
|
|
282
|
+
let entityGenerated = await this.entitiesGeneration.generateEntityFile(tableName, tableData);
|
|
283
|
+
if(!entityGenerated){
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
let entityInfo = this.entitiesGeneration.generatedEntities[tableName];
|
|
287
|
+
let modelGenerated = await this.modelsGeneration.generateModelFile(
|
|
288
|
+
tableName,
|
|
289
|
+
tableData,
|
|
290
|
+
driverKey,
|
|
291
|
+
entityInfo
|
|
292
|
+
);
|
|
293
|
+
if(modelGenerated){
|
|
294
|
+
let modelInfo = this.modelsGeneration.generatedModels[tableName];
|
|
295
|
+
this.generatedEntities[tableName] = Object.assign({}, entityInfo, modelInfo);
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
this.generatedEntities[tableName] = entityInfo;
|
|
940
299
|
}
|
|
941
|
-
this.
|
|
942
|
-
this.
|
|
943
|
-
|
|
300
|
+
let allEntities = Object.assign({}, this.existingEntities, this.generatedEntities);
|
|
301
|
+
this.entitiesConfigGeneration.generateEntitiesConfigFile(
|
|
302
|
+
this.generatedEntities,
|
|
303
|
+
this.existingEntities,
|
|
304
|
+
this.isOverride
|
|
305
|
+
);
|
|
306
|
+
this.entitiesTranslationsGeneration.generateEntitiesTranslationsFile(
|
|
307
|
+
this.generatedEntities,
|
|
308
|
+
this.existingEntities,
|
|
309
|
+
this.isOverride
|
|
310
|
+
);
|
|
311
|
+
this.modelsGeneration.generateRegisteredModelsFile(
|
|
312
|
+
allEntities,
|
|
313
|
+
this.existingModels,
|
|
314
|
+
driverKey,
|
|
315
|
+
this.templates['registered-models']
|
|
316
|
+
);
|
|
944
317
|
return true;
|
|
945
318
|
}
|
|
946
319
|
|
|
@@ -955,7 +328,7 @@ class EntitiesGenerator
|
|
|
955
328
|
Logger.critical('Unsupported driver: '+driverKey);
|
|
956
329
|
return false;
|
|
957
330
|
}
|
|
958
|
-
|
|
331
|
+
let serverConfig = {
|
|
959
332
|
client: sc.get(this.connectionData, 'client', 'mysql2'),
|
|
960
333
|
config: {
|
|
961
334
|
user: sc.get(this.connectionData, 'user', ''),
|
|
@@ -964,7 +337,11 @@ class EntitiesGenerator
|
|
|
964
337
|
host: sc.get(this.connectionData, 'host', 'localhost'),
|
|
965
338
|
port: sc.get(this.connectionData, 'port', 3306)
|
|
966
339
|
}
|
|
967
|
-
}
|
|
340
|
+
};
|
|
341
|
+
if('prisma' === driverKey && this.prismaClient){
|
|
342
|
+
serverConfig.prismaClient = this.prismaClient;
|
|
343
|
+
}
|
|
344
|
+
this.server = new driverClassMapped(serverConfig);
|
|
968
345
|
return this.server;
|
|
969
346
|
}
|
|
970
347
|
|