@reldens/storage 0.54.0 → 0.55.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 +60 -688
- 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/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
|
{
|
|
@@ -49,352 +52,26 @@ class EntitiesGenerator
|
|
|
49
52
|
this.existingEntities = {};
|
|
50
53
|
this.existingEntityFields = {};
|
|
51
54
|
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);
|
|
55
|
+
this.entitiesGeneration = new EntitiesGeneration({
|
|
56
|
+
entitiesFolder: this.entitiesFolder,
|
|
57
|
+
templatePath: this.templates['entity']
|
|
58
|
+
});
|
|
59
|
+
this.modelsGeneration = new ModelsGeneration({
|
|
60
|
+
modelsFolder: this.modelsFolder,
|
|
61
|
+
templates: {
|
|
62
|
+
'objection-js': this.templates['objection-js'],
|
|
63
|
+
'mikro-orm': this.templates['mikro-orm'],
|
|
64
|
+
'prisma': this.templates['prisma']
|
|
394
65
|
}
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
66
|
+
});
|
|
67
|
+
this.entitiesConfigGeneration = new EntitiesConfigGeneration({
|
|
68
|
+
entitiesConfigPath: this.entitiesConfigPath,
|
|
69
|
+
templatePath: this.templates['entities-config']
|
|
70
|
+
});
|
|
71
|
+
this.entitiesTranslationsGeneration = new EntitiesTranslationsGeneration({
|
|
72
|
+
entitiesTranslationsPath: this.entitiesTranslationsPath,
|
|
73
|
+
templatePath: this.templates['entities-translations']
|
|
74
|
+
});
|
|
398
75
|
}
|
|
399
76
|
|
|
400
77
|
detectExistingEntities()
|
|
@@ -411,7 +88,6 @@ class EntitiesGenerator
|
|
|
411
88
|
this.existingEntities[tableName] = {entityFileName: file, tableName};
|
|
412
89
|
this.detectExistingEntityFields(tableName, file);
|
|
413
90
|
}
|
|
414
|
-
this.detectExistingModels();
|
|
415
91
|
Logger.info('Detected '+Object.keys(this.existingEntities).length+' existing entities.');
|
|
416
92
|
}
|
|
417
93
|
|
|
@@ -438,6 +114,7 @@ class EntitiesGenerator
|
|
|
438
114
|
};
|
|
439
115
|
}
|
|
440
116
|
}
|
|
117
|
+
Logger.info('Detected '+Object.keys(this.existingModels).length+' existing models.');
|
|
441
118
|
}
|
|
442
119
|
|
|
443
120
|
detectExistingEntityFields(tableName, fileName)
|
|
@@ -552,11 +229,7 @@ class EntitiesGenerator
|
|
|
552
229
|
|
|
553
230
|
entityExistsInConfig(tableName)
|
|
554
231
|
{
|
|
555
|
-
|
|
556
|
-
return false;
|
|
557
|
-
}
|
|
558
|
-
let configContent = FileHandler.readFile(this.entitiesConfigPath);
|
|
559
|
-
return configContent && configContent.includes(sc.camelCase(tableName)+':');
|
|
232
|
+
return this.entitiesConfigGeneration.entityExistsInConfig(tableName, FileHandler.readFile(this.entitiesConfigPath));
|
|
560
233
|
}
|
|
561
234
|
|
|
562
235
|
modelExistsInRegistered(tableName, driverKey)
|
|
@@ -569,337 +242,6 @@ class EntitiesGenerator
|
|
|
569
242
|
return registeredContent && registeredContent.includes(sc.camelCase(tableName)+':');
|
|
570
243
|
}
|
|
571
244
|
|
|
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
245
|
async generate()
|
|
904
246
|
{
|
|
905
247
|
if(!this.server){
|
|
@@ -920,6 +262,7 @@ class EntitiesGenerator
|
|
|
920
262
|
return false;
|
|
921
263
|
}
|
|
922
264
|
this.detectExistingEntities();
|
|
265
|
+
this.detectExistingModels();
|
|
923
266
|
let driverKey = sc.get(
|
|
924
267
|
this.connectionData,
|
|
925
268
|
'driver',
|
|
@@ -935,12 +278,41 @@ class EntitiesGenerator
|
|
|
935
278
|
}
|
|
936
279
|
for(let tableName of Object.keys(tablesToGenerate)){
|
|
937
280
|
let tableData = tablesToGenerate[tableName];
|
|
938
|
-
await this.generateEntityFile(tableName, tableData);
|
|
939
|
-
|
|
281
|
+
let entityGenerated = await this.entitiesGeneration.generateEntityFile(tableName, tableData);
|
|
282
|
+
if(!entityGenerated){
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
let entityInfo = this.entitiesGeneration.generatedEntities[tableName];
|
|
286
|
+
let modelGenerated = await this.modelsGeneration.generateModelFile(
|
|
287
|
+
tableName,
|
|
288
|
+
tableData,
|
|
289
|
+
driverKey,
|
|
290
|
+
entityInfo
|
|
291
|
+
);
|
|
292
|
+
if(modelGenerated){
|
|
293
|
+
let modelInfo = this.modelsGeneration.generatedModels[tableName];
|
|
294
|
+
this.generatedEntities[tableName] = Object.assign({}, entityInfo, modelInfo);
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
this.generatedEntities[tableName] = entityInfo;
|
|
940
298
|
}
|
|
941
|
-
this.
|
|
942
|
-
this.
|
|
943
|
-
|
|
299
|
+
let allEntities = Object.assign({}, this.existingEntities, this.generatedEntities);
|
|
300
|
+
this.entitiesConfigGeneration.generateEntitiesConfigFile(
|
|
301
|
+
this.generatedEntities,
|
|
302
|
+
this.existingEntities,
|
|
303
|
+
this.isOverride
|
|
304
|
+
);
|
|
305
|
+
this.entitiesTranslationsGeneration.generateEntitiesTranslationsFile(
|
|
306
|
+
this.generatedEntities,
|
|
307
|
+
this.existingEntities,
|
|
308
|
+
this.isOverride
|
|
309
|
+
);
|
|
310
|
+
this.modelsGeneration.generateRegisteredModelsFile(
|
|
311
|
+
allEntities,
|
|
312
|
+
this.existingModels,
|
|
313
|
+
driverKey,
|
|
314
|
+
this.templates['registered-models']
|
|
315
|
+
);
|
|
944
316
|
return true;
|
|
945
317
|
}
|
|
946
318
|
|