@reldens/storage 0.53.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/README.md +28 -21
- package/bin/generate-prisma-schema.js +132 -63
- package/bin/reldens-storage.js +106 -51
- 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/lib/prisma/prisma-schema-generator.js +27 -45
- package/package.json +1 -1
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Reldens - ModelsGeneration
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { FileHandler } = require('@reldens/server-utils');
|
|
8
|
+
const { Logger, sc } = require('@reldens/utils');
|
|
9
|
+
const { TypeMapper } = require('../type-mapper');
|
|
10
|
+
const { BaseGenerator } = require('./base-generator');
|
|
11
|
+
|
|
12
|
+
class ModelsGeneration extends BaseGenerator
|
|
13
|
+
{
|
|
14
|
+
|
|
15
|
+
constructor(props)
|
|
16
|
+
{
|
|
17
|
+
super();
|
|
18
|
+
this.modelsFolder = sc.get(props, 'modelsFolder', '');
|
|
19
|
+
this.templates = sc.get(props, 'templates', {});
|
|
20
|
+
this.generatedModels = {};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async generateModelFile(tableName, tableData, driverKey, entityInfo)
|
|
24
|
+
{
|
|
25
|
+
let modelTemplatePath = this.templates[driverKey];
|
|
26
|
+
if(!FileHandler.exists(modelTemplatePath)){
|
|
27
|
+
Logger.critical('Model template file "'+modelTemplatePath+'" not found for driver "'+driverKey+'".');
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
let modelTemplateContent = FileHandler.fetchFileContents(modelTemplatePath);
|
|
31
|
+
if(!modelTemplateContent){
|
|
32
|
+
Logger.critical('Failed to read model template file: '+modelTemplatePath);
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
let modelClassName = sc.capitalizedCamelCase(tableName)+'Model';
|
|
36
|
+
let modelPropertiesList = Object.keys(tableData.columns).join(', ');
|
|
37
|
+
let modelPropertiesConstructor = Object.keys(tableData.columns)
|
|
38
|
+
.map(columnName => 'this.'+columnName+' = '+columnName+';')
|
|
39
|
+
.join('\n ');
|
|
40
|
+
let modelRelations = '';
|
|
41
|
+
let entityPropertiesDefinition = this.getEntityPropertiesDefinition(tableData.columns, driverKey);
|
|
42
|
+
let replacements = {
|
|
43
|
+
modelClassName,
|
|
44
|
+
tableName: 'prisma' === driverKey ? tableName.toLowerCase() : tableName,
|
|
45
|
+
modelPropertiesList,
|
|
46
|
+
modelPropertiesConstructor,
|
|
47
|
+
modelRelations,
|
|
48
|
+
entityPropertiesDefinition
|
|
49
|
+
};
|
|
50
|
+
let modelContent = this.applyReplacements(modelTemplateContent, replacements);
|
|
51
|
+
let fileName = sc.kebabCase(tableName)+'-model.js';
|
|
52
|
+
let driverFolder = FileHandler.joinPaths(this.modelsFolder, driverKey);
|
|
53
|
+
FileHandler.createFolder(driverFolder);
|
|
54
|
+
let filePath = FileHandler.joinPaths(driverFolder, fileName);
|
|
55
|
+
if(!FileHandler.writeFile(filePath, modelContent)){
|
|
56
|
+
Logger.critical('Failed to write model file: '+fileName);
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
Logger.info('Generated model file: '+fileName);
|
|
60
|
+
this.generatedModels[tableName] = {
|
|
61
|
+
modelClassName,
|
|
62
|
+
modelFileName: fileName,
|
|
63
|
+
driverKey
|
|
64
|
+
};
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
getEntityPropertiesDefinition(columns, driverKey)
|
|
69
|
+
{
|
|
70
|
+
if('mikro-orm' === driverKey){
|
|
71
|
+
let entityProps = [];
|
|
72
|
+
for(let columnName of Object.keys(columns)){
|
|
73
|
+
let column = columns[columnName];
|
|
74
|
+
let isPrimary = 'PRI' === column.key;
|
|
75
|
+
let type = TypeMapper.mapDbTypeToJsType(column.type);
|
|
76
|
+
let propDef = columnName+': { type: \''+type+'\'';
|
|
77
|
+
if(isPrimary){
|
|
78
|
+
propDef += ', primary: true';
|
|
79
|
+
}
|
|
80
|
+
if(column.nullable){
|
|
81
|
+
propDef += ', nullable: true';
|
|
82
|
+
}
|
|
83
|
+
propDef += ' }';
|
|
84
|
+
entityProps.push(propDef);
|
|
85
|
+
}
|
|
86
|
+
return entityProps.join(',\n ');
|
|
87
|
+
}
|
|
88
|
+
if('prisma' === driverKey){
|
|
89
|
+
let prismaProps = [];
|
|
90
|
+
for(let columnName of Object.keys(columns)){
|
|
91
|
+
let column = columns[columnName];
|
|
92
|
+
let prismaType = TypeMapper.mapDbTypeToPrismaType(column.type);
|
|
93
|
+
let isPrimary = 'PRI' === column.key;
|
|
94
|
+
let propDef = columnName+': {\n type: \''+prismaType+'\'';
|
|
95
|
+
if(isPrimary){
|
|
96
|
+
propDef += ',\n id: true';
|
|
97
|
+
}
|
|
98
|
+
if(column.nullable){
|
|
99
|
+
propDef += ',\n optional: true';
|
|
100
|
+
}
|
|
101
|
+
propDef += '\n }';
|
|
102
|
+
prismaProps.push(propDef);
|
|
103
|
+
}
|
|
104
|
+
return prismaProps.join(',\n ');
|
|
105
|
+
}
|
|
106
|
+
return '';
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
generateRegisteredModelsFile(entitiesInfo, existingModels, driverKey, templatePath)
|
|
110
|
+
{
|
|
111
|
+
if(!FileHandler.exists(templatePath)){
|
|
112
|
+
Logger.critical('Registered models template file not found: '+templatePath);
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
let registeredTemplateContent = FileHandler.fetchFileContents(templatePath);
|
|
116
|
+
if(!registeredTemplateContent){
|
|
117
|
+
Logger.critical('Failed to read registered models template file: '+templatePath);
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
let allEntities = Object.assign({}, entitiesInfo);
|
|
121
|
+
let replacements = {
|
|
122
|
+
registeredModels: this.getRegisteredModels(allEntities, existingModels, driverKey),
|
|
123
|
+
registeredEntitiesObject: this.getRegisteredEntitiesObject(allEntities, existingModels, driverKey)
|
|
124
|
+
};
|
|
125
|
+
let registeredContent = this.applyReplacements(registeredTemplateContent, replacements);
|
|
126
|
+
let driverFolder = FileHandler.joinPaths(this.modelsFolder, driverKey);
|
|
127
|
+
let filePath = FileHandler.joinPaths(driverFolder, 'registered-models-'+driverKey+'.js');
|
|
128
|
+
if(!FileHandler.writeFile(filePath, registeredContent)){
|
|
129
|
+
Logger.critical('Failed to write registered models file: '+filePath);
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
Logger.info('Generated registered models file: '+filePath);
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
getRegisteredModels(allEntities, existingModels, driverKey)
|
|
137
|
+
{
|
|
138
|
+
let registeredModels = [];
|
|
139
|
+
for(let tableName of Object.keys(allEntities)){
|
|
140
|
+
let entity = allEntities[tableName];
|
|
141
|
+
if(entity.driverKey && driverKey !== entity.driverKey){
|
|
142
|
+
if(!existingModels[tableName] || !existingModels[tableName][driverKey]){
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
let modelClassName = entity.modelClassName || sc.capitalizedCamelCase(tableName)+'Model';
|
|
147
|
+
let modelFileName = entity.modelFileName || sc.kebabCase(tableName)+'-model.js';
|
|
148
|
+
registeredModels.push(
|
|
149
|
+
'const { '+modelClassName+' } = require(\'./'+modelFileName.replace('.js', '')+'\');'
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
return registeredModels.join('\n');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
getRegisteredEntitiesObject(allEntities, existingModels, driverKey)
|
|
156
|
+
{
|
|
157
|
+
let registeredEntitiesObject = [];
|
|
158
|
+
for(let tableName of Object.keys(allEntities)){
|
|
159
|
+
let entity = allEntities[tableName];
|
|
160
|
+
if(entity.driverKey && driverKey !== entity.driverKey){
|
|
161
|
+
if(!existingModels[tableName] || !existingModels[tableName][driverKey]){
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
let modelClassName = entity.modelClassName || sc.capitalizedCamelCase(tableName)+'Model';
|
|
166
|
+
registeredEntitiesObject.push(sc.camelCase(tableName)+': '+modelClassName);
|
|
167
|
+
}
|
|
168
|
+
return registeredEntitiesObject.join(',\n ');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
module.exports.ModelsGeneration = ModelsGeneration;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
const { execSync
|
|
7
|
+
const { execSync } = require('child_process');
|
|
8
8
|
const { FileHandler } = require('@reldens/server-utils');
|
|
9
9
|
const { Logger, sc } = require('@reldens/utils');
|
|
10
10
|
|
|
@@ -21,7 +21,7 @@ class PrismaSchemaGenerator
|
|
|
21
21
|
this.maxWaitTime = sc.get(props, 'maxWaitTime', 30000);
|
|
22
22
|
this.prismaSchemaPath = sc.get(props, 'prismaSchemaPath', FileHandler.joinPaths(process.cwd(), 'prisma'));
|
|
23
23
|
this.schemaFilePath = FileHandler.joinPaths(this.prismaSchemaPath, 'schema.prisma');
|
|
24
|
-
this.clientOutputPath = sc.get(props, 'clientOutputPath', '
|
|
24
|
+
this.clientOutputPath = sc.get(props, 'clientOutputPath', '');
|
|
25
25
|
this.generateBinaryTargets = sc.get(props, 'generateBinaryTargets', ['native']);
|
|
26
26
|
}
|
|
27
27
|
|
|
@@ -30,62 +30,41 @@ class PrismaSchemaGenerator
|
|
|
30
30
|
FileHandler.createFolder(this.prismaSchemaPath);
|
|
31
31
|
this.generateSchemaFile();
|
|
32
32
|
Logger.info('Running prisma introspect "npx prisma db pull"...');
|
|
33
|
-
|
|
33
|
+
try {
|
|
34
|
+
execSync('npx prisma db pull', { stdio: 'inherit' });
|
|
35
|
+
} catch(error) {
|
|
36
|
+
Logger.critical('Failed to pull database schema: ' + error.message);
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
34
39
|
let generateCommand = 'npx prisma generate';
|
|
35
40
|
if(this.dataProxy){
|
|
36
41
|
generateCommand += ' --data-proxy';
|
|
37
42
|
}
|
|
38
43
|
Logger.info('Running prisma generate "'+generateCommand+'"...');
|
|
39
|
-
await this.executeWithFallback(generateCommand);
|
|
40
|
-
await this.waitForSchemaGeneration();
|
|
41
|
-
return true;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
async executeWithFallback(command)
|
|
45
|
-
{
|
|
46
44
|
try {
|
|
47
|
-
execSync(
|
|
45
|
+
execSync(generateCommand, { stdio: 'inherit' });
|
|
48
46
|
} catch(error) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
47
|
+
let errorString = error.toString();
|
|
48
|
+
if(errorString.includes('EPERM') && errorString.includes('query_engine-windows.dll.node')){
|
|
49
|
+
Logger.warning('Windows permission error detected with query engine file.');
|
|
50
|
+
Logger.warning('This is a known Prisma issue on Windows.');
|
|
51
|
+
Logger.warning('Please manually run: ' + generateCommand);
|
|
52
|
+
Logger.warning('You may need to close any processes using the Prisma client first.');
|
|
53
|
+
return false;
|
|
53
54
|
}
|
|
54
|
-
Logger.critical('
|
|
55
|
+
Logger.critical('Generate command failed: ' + error.message);
|
|
55
56
|
return false;
|
|
56
57
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
isWindowsPermissionError(error)
|
|
60
|
-
{
|
|
61
|
-
return error.message
|
|
62
|
-
&& error.message.includes('EPERM')
|
|
63
|
-
&& error.message.includes('rename')
|
|
64
|
-
&& error.message.includes('query_engine-windows.dll.node');
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
executeDetached(command)
|
|
68
|
-
{
|
|
69
|
-
return new Promise((resolve, reject) => {
|
|
70
|
-
let child = spawn(command, [], {
|
|
71
|
-
stdio: 'inherit',
|
|
72
|
-
shell: true,
|
|
73
|
-
detached: true
|
|
74
|
-
});
|
|
75
|
-
child.on('close', (code) => {
|
|
76
|
-
if(0 === code){
|
|
77
|
-
resolve();
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
reject(new Error('Command failed with exit code ' + code));
|
|
81
|
-
});
|
|
82
|
-
child.on('error', reject);
|
|
83
|
-
});
|
|
58
|
+
await this.waitForSchemaGeneration();
|
|
59
|
+
return true;
|
|
84
60
|
}
|
|
85
61
|
|
|
86
62
|
async waitForSchemaGeneration()
|
|
87
63
|
{
|
|
88
|
-
let
|
|
64
|
+
let clientPath = this.clientOutputPath || 'node_modules/.prisma/client';
|
|
65
|
+
let generatedClientPath = clientPath.startsWith('node_modules')
|
|
66
|
+
? FileHandler.joinPaths(process.cwd(), clientPath)
|
|
67
|
+
: FileHandler.joinPaths(this.prismaSchemaPath, clientPath);
|
|
89
68
|
let clientIndexPath = FileHandler.joinPaths(generatedClientPath, 'index.js');
|
|
90
69
|
let startTime = Date.now();
|
|
91
70
|
return new Promise((resolve) => {
|
|
@@ -160,7 +139,10 @@ class PrismaSchemaGenerator
|
|
|
160
139
|
{
|
|
161
140
|
let generatorContent = 'generator client {\n';
|
|
162
141
|
generatorContent += ' provider = "prisma-client-js"\n';
|
|
163
|
-
|
|
142
|
+
if(this.clientOutputPath){
|
|
143
|
+
let normalizedPath = this.clientOutputPath.replace(/\\/g, '/');
|
|
144
|
+
generatorContent += ' output = "' + normalizedPath + '"\n';
|
|
145
|
+
}
|
|
164
146
|
if(0 < this.generateBinaryTargets.length){
|
|
165
147
|
generatorContent += ' binaryTargets = [' +
|
|
166
148
|
this.generateBinaryTargets.map(target => '"' + target + '"').join(', ')
|