@mikro-orm/entity-generator 7.0.4-dev.8 → 7.0.4
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/CoreImportsHelper.d.ts +10 -1
- package/CoreImportsHelper.js +1 -10
- package/DefineEntitySourceFile.d.ts +2 -2
- package/DefineEntitySourceFile.js +140 -139
- package/EntityGenerator.d.ts +12 -12
- package/EntityGenerator.js +377 -343
- package/EntitySchemaSourceFile.d.ts +5 -5
- package/EntitySchemaSourceFile.js +139 -143
- package/NativeEnumSourceFile.d.ts +14 -8
- package/NativeEnumSourceFile.js +43 -41
- package/README.md +1 -1
- package/SourceFile.d.ts +59 -41
- package/SourceFile.js +987 -919
- package/package.json +4 -4
package/SourceFile.js
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
Cascade,
|
|
3
|
+
Config,
|
|
4
|
+
DecimalType,
|
|
5
|
+
ReferenceKind,
|
|
6
|
+
SCALAR_TYPES,
|
|
7
|
+
UnknownType,
|
|
8
|
+
Utils,
|
|
9
|
+
inspect,
|
|
10
|
+
} from '@mikro-orm/core';
|
|
2
11
|
import { parse, relative } from 'node:path';
|
|
3
12
|
import { POSSIBLE_TYPE_IMPORTS } from './CoreImportsHelper.js';
|
|
4
13
|
/**
|
|
@@ -7,944 +16,1003 @@ import { POSSIBLE_TYPE_IMPORTS } from './CoreImportsHelper.js';
|
|
|
7
16
|
export const identifierRegex = /^(?:[$_\p{ID_Start}])(?:[$\u200C\u200D\p{ID_Continue}])*$/u;
|
|
8
17
|
const primitivesAndLibs = [...SCALAR_TYPES, 'unknown', 'object', 'any'];
|
|
9
18
|
export class SourceFile {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
for (const index of this.meta.indexes) {
|
|
37
|
-
if (index.properties?.length === 1 && typeof this.meta.properties[index.properties[0]] !== 'undefined') {
|
|
38
|
-
continue;
|
|
39
|
-
}
|
|
40
|
-
ret += `@${this.referenceDecoratorImport('Index')}(${this.serializeObject(this.getIndexOptions(index))})\n`;
|
|
41
|
-
}
|
|
42
|
-
for (const index of this.meta.uniques) {
|
|
43
|
-
if (index.properties?.length === 1 && typeof this.meta.properties[index.properties[0]] !== 'undefined') {
|
|
44
|
-
continue;
|
|
45
|
-
}
|
|
46
|
-
ret += `@${this.referenceDecoratorImport('Unique')}(${this.serializeObject(this.getUniqueOptions(index))})\n`;
|
|
47
|
-
}
|
|
48
|
-
let classHead = '';
|
|
49
|
-
if (this.meta.className === this.options.customBaseEntityName) {
|
|
50
|
-
const defineConfigTypeSettings = {};
|
|
51
|
-
defineConfigTypeSettings.forceObject = this.platform.getConfig().get('serialization').forceObject ?? false;
|
|
52
|
-
classHead += `\n${' '.repeat(2)}[${this.referenceCoreImport('Config')}]?: ${this.referenceCoreImport('DefineConfig')}<${this.serializeObject(defineConfigTypeSettings)}>;\n\n`;
|
|
53
|
-
}
|
|
54
|
-
if (this.meta.repositoryClass) {
|
|
55
|
-
this.entityImports.add(this.meta.repositoryClass);
|
|
56
|
-
classHead += `\n${' '.repeat(2)}[${this.referenceCoreImport('EntityRepositoryType')}]?: ${this.meta.repositoryClass};\n`;
|
|
57
|
-
}
|
|
58
|
-
const enumDefinitions = [];
|
|
59
|
-
const eagerProperties = [];
|
|
60
|
-
const primaryProps = [];
|
|
61
|
-
let classBody = '';
|
|
62
|
-
Object.values(this.meta.properties).forEach(prop => {
|
|
63
|
-
const decorator = this.getPropertyDecorator(prop, 2);
|
|
64
|
-
const definition = this.getPropertyDefinition(prop, 2);
|
|
65
|
-
classBody += decorator;
|
|
66
|
-
classBody += definition;
|
|
67
|
-
classBody += '\n';
|
|
68
|
-
if (prop.enum) {
|
|
69
|
-
const def = this.getEnumClassDefinition(prop, 2);
|
|
70
|
-
if (def.length) {
|
|
71
|
-
enumDefinitions.push(def);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
if (prop.eager) {
|
|
75
|
-
eagerProperties.push(prop);
|
|
76
|
-
}
|
|
77
|
-
if (prop.primary && (!['id', '_id', 'uuid'].includes(prop.name) || this.meta.compositePK)) {
|
|
78
|
-
primaryProps.push(prop);
|
|
79
|
-
}
|
|
80
|
-
});
|
|
81
|
-
if (primaryProps.length > 0) {
|
|
82
|
-
const primaryPropNames = primaryProps.map(prop => `'${prop.name}'`);
|
|
83
|
-
if (primaryProps.length > 1) {
|
|
84
|
-
classHead += `\n${' '.repeat(2)}[${this.referenceCoreImport('PrimaryKeyProp')}]?: [${primaryPropNames.join(', ')}];\n`;
|
|
85
|
-
}
|
|
86
|
-
else {
|
|
87
|
-
classHead += `\n${' '.repeat(2)}[${this.referenceCoreImport('PrimaryKeyProp')}]?: ${primaryPropNames[0]};\n`;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
if (eagerProperties.length > 0) {
|
|
91
|
-
const eagerPropertyNames = eagerProperties.map(prop => `'${prop.name}'`).sort();
|
|
92
|
-
classHead += `\n${' '.repeat(2)}[${this.referenceCoreImport('EagerProps')}]?: ${eagerPropertyNames.join(' | ')};\n`;
|
|
93
|
-
}
|
|
94
|
-
ret += this.getEntityClass(classBody ? `${classHead}\n${classBody}` : classHead);
|
|
95
|
-
ret = `${this.generateImports()}\n\n${enumDefinitions.length ? enumDefinitions.join('\n') + '\n' : ''}${ret}`;
|
|
96
|
-
return ret;
|
|
97
|
-
}
|
|
98
|
-
/**
|
|
99
|
-
* Convert index column options to quoted output format.
|
|
100
|
-
*/
|
|
101
|
-
getColumnOptions(columns) {
|
|
102
|
-
if (!columns?.length) {
|
|
103
|
-
return undefined;
|
|
104
|
-
}
|
|
105
|
-
return columns.map(col => {
|
|
106
|
-
const colOpt = { name: this.quote(col.name) };
|
|
107
|
-
if (col.sort) {
|
|
108
|
-
colOpt.sort = this.quote(col.sort.toUpperCase());
|
|
109
|
-
}
|
|
110
|
-
if (col.nulls) {
|
|
111
|
-
colOpt.nulls = this.quote(col.nulls.toUpperCase());
|
|
112
|
-
}
|
|
113
|
-
if (col.length != null) {
|
|
114
|
-
colOpt.length = col.length;
|
|
115
|
-
}
|
|
116
|
-
if (col.collation) {
|
|
117
|
-
colOpt.collation = this.quote(col.collation);
|
|
118
|
-
}
|
|
119
|
-
return colOpt;
|
|
120
|
-
});
|
|
19
|
+
meta;
|
|
20
|
+
namingStrategy;
|
|
21
|
+
platform;
|
|
22
|
+
options;
|
|
23
|
+
coreImports = new Set();
|
|
24
|
+
decoratorImports = new Set();
|
|
25
|
+
entityImports = new Set();
|
|
26
|
+
enumImports = new Map();
|
|
27
|
+
constructor(meta, namingStrategy, platform, options) {
|
|
28
|
+
this.meta = meta;
|
|
29
|
+
this.namingStrategy = namingStrategy;
|
|
30
|
+
this.platform = platform;
|
|
31
|
+
this.options = options;
|
|
32
|
+
}
|
|
33
|
+
generate() {
|
|
34
|
+
let ret = '';
|
|
35
|
+
if (this.meta.embeddable || this.meta.collection) {
|
|
36
|
+
if (this.meta.embeddable) {
|
|
37
|
+
const options = this.getEmbeddableDeclOptions();
|
|
38
|
+
ret += `@${this.referenceDecoratorImport('Embeddable')}(${Utils.hasObjectKeys(options) ? this.serializeObject(options) : ''})\n`;
|
|
39
|
+
} else {
|
|
40
|
+
const options = this.getEntityDeclOptions();
|
|
41
|
+
ret += `@${this.referenceDecoratorImport('Entity')}(${Utils.hasObjectKeys(options) ? this.serializeObject(options) : ''})\n`;
|
|
42
|
+
}
|
|
121
43
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
if (typeof index.expression === 'string') {
|
|
128
|
-
indexOpt.expression = this.quote(index.expression);
|
|
129
|
-
}
|
|
130
|
-
else if (typeof index.expression === 'function') {
|
|
131
|
-
indexOpt.expression = `${index.expression}`.replace(')=>`', ') => `');
|
|
132
|
-
}
|
|
133
|
-
if (isAtEntityLevel && index.properties) {
|
|
134
|
-
indexOpt.properties = Utils.asArray(index.properties).map(prop => this.quote('' + prop));
|
|
135
|
-
}
|
|
136
|
-
// Index type (e.g., 'fulltext', 'spatial', 'btree', 'hash')
|
|
137
|
-
if (index.type) {
|
|
138
|
-
indexOpt.type = this.quote(index.type);
|
|
139
|
-
}
|
|
140
|
-
// Advanced index options
|
|
141
|
-
const columns = this.getColumnOptions(index.columns);
|
|
142
|
-
if (columns) {
|
|
143
|
-
indexOpt.columns = columns;
|
|
144
|
-
}
|
|
145
|
-
if (index.include) {
|
|
146
|
-
indexOpt.include = Utils.asArray(index.include).map(prop => this.quote('' + prop));
|
|
147
|
-
}
|
|
148
|
-
if (index.fillFactor != null) {
|
|
149
|
-
indexOpt.fillFactor = index.fillFactor;
|
|
150
|
-
}
|
|
151
|
-
if (index.invisible) {
|
|
152
|
-
indexOpt.invisible = true;
|
|
153
|
-
}
|
|
154
|
-
if (index.disabled) {
|
|
155
|
-
indexOpt.disabled = true;
|
|
156
|
-
}
|
|
157
|
-
if (index.clustered) {
|
|
158
|
-
indexOpt.clustered = true;
|
|
159
|
-
}
|
|
160
|
-
return indexOpt;
|
|
44
|
+
for (const index of this.meta.indexes) {
|
|
45
|
+
if (index.properties?.length === 1 && typeof this.meta.properties[index.properties[0]] !== 'undefined') {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
ret += `@${this.referenceDecoratorImport('Index')}(${this.serializeObject(this.getIndexOptions(index))})\n`;
|
|
161
49
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
if (typeof index.expression === 'string') {
|
|
168
|
-
uniqueOpt.expression = this.quote(index.expression);
|
|
169
|
-
}
|
|
170
|
-
else if (typeof index.expression === 'function') {
|
|
171
|
-
uniqueOpt.expression = `${index.expression}`.replace(')=>`', ') => `');
|
|
172
|
-
}
|
|
173
|
-
if (isAtEntityLevel && index.properties) {
|
|
174
|
-
uniqueOpt.properties = Utils.asArray(index.properties).map(prop => this.quote('' + prop));
|
|
175
|
-
}
|
|
176
|
-
if (index.deferMode) {
|
|
177
|
-
uniqueOpt.deferMode =
|
|
178
|
-
`${this.referenceCoreImport('DeferMode')}.INITIALLY_${index.deferMode.toUpperCase()}`;
|
|
179
|
-
}
|
|
180
|
-
const columns = this.getColumnOptions(index.columns);
|
|
181
|
-
if (columns) {
|
|
182
|
-
uniqueOpt.columns = columns;
|
|
183
|
-
}
|
|
184
|
-
if (index.include) {
|
|
185
|
-
uniqueOpt.include = Utils.asArray(index.include).map(prop => this.quote('' + prop));
|
|
186
|
-
}
|
|
187
|
-
if (index.fillFactor != null) {
|
|
188
|
-
uniqueOpt.fillFactor = index.fillFactor;
|
|
189
|
-
}
|
|
190
|
-
if (index.disabled) {
|
|
191
|
-
uniqueOpt.disabled = true;
|
|
192
|
-
}
|
|
193
|
-
return uniqueOpt;
|
|
194
|
-
}
|
|
195
|
-
generateImports() {
|
|
196
|
-
const imports = new Set();
|
|
197
|
-
if (this.coreImports.size > 0) {
|
|
198
|
-
imports.add(`import { ${[...this.coreImports]
|
|
199
|
-
.sort()
|
|
200
|
-
.map(t => {
|
|
201
|
-
let ret = POSSIBLE_TYPE_IMPORTS.includes(t) ? `type ${t}` : t;
|
|
202
|
-
if (this.options.coreImportsPrefix) {
|
|
203
|
-
const resolvedIdentifier = `${this.options.coreImportsPrefix}${t}`;
|
|
204
|
-
ret += ` as ${resolvedIdentifier}`;
|
|
205
|
-
}
|
|
206
|
-
return ret;
|
|
207
|
-
})
|
|
208
|
-
.join(', ')} } from '@mikro-orm/core';`);
|
|
209
|
-
}
|
|
210
|
-
if (this.decoratorImports.size > 0) {
|
|
211
|
-
const type = this.options.decorators;
|
|
212
|
-
imports.add(`import { ${[...this.decoratorImports]
|
|
213
|
-
.sort()
|
|
214
|
-
.map(t => {
|
|
215
|
-
let ret = t;
|
|
216
|
-
if (this.options.coreImportsPrefix) {
|
|
217
|
-
const resolvedIdentifier = `${this.options.coreImportsPrefix}${t}`;
|
|
218
|
-
ret += ` as ${resolvedIdentifier}`;
|
|
219
|
-
}
|
|
220
|
-
return ret;
|
|
221
|
-
})
|
|
222
|
-
.join(', ')} } from '@mikro-orm/decorators/${type}';`);
|
|
223
|
-
}
|
|
224
|
-
const extension = this.options.esmImport ? '.js' : '';
|
|
225
|
-
const { dir, base } = parse(`${this.options.path ?? '.'}/${this.getBaseName()}`);
|
|
226
|
-
const basePath = relative(dir, this.options.path ?? '.') || '.';
|
|
227
|
-
(this.options.extraImports?.(basePath, base) ?? []).forEach(v => this.entityImports.add(v));
|
|
228
|
-
const entityImports = [...this.entityImports].filter(e => e !== this.meta.className);
|
|
229
|
-
const importMap = new Map();
|
|
230
|
-
for (const entity of entityImports) {
|
|
231
|
-
const file = this.options.onImport?.(entity, basePath, extension, base) ?? {
|
|
232
|
-
path: `${basePath}/${this.options.fileName(entity)}${extension}`,
|
|
233
|
-
name: entity,
|
|
234
|
-
};
|
|
235
|
-
if (file.path === '') {
|
|
236
|
-
if (file.name === '') {
|
|
237
|
-
continue;
|
|
238
|
-
}
|
|
239
|
-
importMap.set(file.path, `import ${this.quote(file.name)};`);
|
|
240
|
-
continue;
|
|
241
|
-
}
|
|
242
|
-
if (file.name === '') {
|
|
243
|
-
importMap.set(file.path, `import * as ${entity} from ${this.quote(file.path)};`);
|
|
244
|
-
continue;
|
|
245
|
-
}
|
|
246
|
-
if (file.name === 'default') {
|
|
247
|
-
importMap.set(file.path, `import ${entity} from ${this.quote(file.path)};`);
|
|
248
|
-
continue;
|
|
249
|
-
}
|
|
250
|
-
if (file.name === entity) {
|
|
251
|
-
importMap.set(file.path, `import { ${entity} } from ${this.quote(file.path)};`);
|
|
252
|
-
continue;
|
|
253
|
-
}
|
|
254
|
-
importMap.set(file.path, `import { ${identifierRegex.test(file.name) ? file.name : this.quote(file.name)} as ${entity} } from ${this.quote(file.path)};`);
|
|
255
|
-
}
|
|
256
|
-
if (this.enumImports.size) {
|
|
257
|
-
for (const [name, exports] of this.enumImports.entries()) {
|
|
258
|
-
const file = this.options.onImport?.(name, basePath, extension, base) ?? {
|
|
259
|
-
path: `${basePath}/${this.options.fileName(name)}${extension}`,
|
|
260
|
-
name,
|
|
261
|
-
};
|
|
262
|
-
importMap.set(file.path, `import { ${exports.join(', ')} } from ${this.quote(file.path)};`);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
for (const key of [...importMap.keys()].sort()) {
|
|
266
|
-
imports.add(importMap.get(key));
|
|
267
|
-
}
|
|
268
|
-
return Array.from(imports.values()).join('\n');
|
|
50
|
+
for (const index of this.meta.uniques) {
|
|
51
|
+
if (index.properties?.length === 1 && typeof this.meta.properties[index.properties[0]] !== 'undefined') {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
ret += `@${this.referenceDecoratorImport('Unique')}(${this.serializeObject(this.getUniqueOptions(index))})\n`;
|
|
269
55
|
}
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
ret += `class ${this.meta.className}`;
|
|
276
|
-
if (this.meta.extends) {
|
|
277
|
-
this.entityImports.add(Utils.className(this.meta.extends));
|
|
278
|
-
ret += ` extends ${Utils.className(this.meta.extends)}`;
|
|
279
|
-
}
|
|
280
|
-
else if (this.options.useCoreBaseEntity) {
|
|
281
|
-
ret += ` extends ${this.referenceCoreImport('BaseEntity')}`;
|
|
282
|
-
}
|
|
283
|
-
ret += ` {\n${classBody}}\n`;
|
|
284
|
-
return ret;
|
|
285
|
-
}
|
|
286
|
-
getBaseName(extension = '.ts') {
|
|
287
|
-
return `${this.options.fileName(this.meta.className)}${extension}`;
|
|
288
|
-
}
|
|
289
|
-
quote(val) {
|
|
290
|
-
const backtick = val.startsWith(`'`) || val.includes('\n');
|
|
291
|
-
/* v8 ignore next */
|
|
292
|
-
return backtick ? `\`${val.replaceAll('`', '\\``')}\`` : `'${val.replaceAll(`'`, `\\'`)}'`;
|
|
293
|
-
}
|
|
294
|
-
getPropertyDefinition(prop, padLeft) {
|
|
295
|
-
const padding = ' '.repeat(padLeft);
|
|
296
|
-
const propName = identifierRegex.test(prop.name) ? prop.name : this.quote(prop.name);
|
|
297
|
-
const enumMode = this.options.enumMode;
|
|
298
|
-
let hiddenType = '';
|
|
299
|
-
if (prop.hidden) {
|
|
300
|
-
hiddenType += ` & ${this.referenceCoreImport('Hidden')}`;
|
|
301
|
-
}
|
|
302
|
-
if ([ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(prop.kind)) {
|
|
303
|
-
return `${padding}${propName}${hiddenType ? `: ${this.referenceCoreImport('Collection')}<${prop.type}>${hiddenType}` : ''} = new ${this.referenceCoreImport('Collection')}<${prop.type}>(this);\n`;
|
|
304
|
-
}
|
|
305
|
-
const isScalar = typeof prop.kind === 'undefined' || prop.kind === ReferenceKind.SCALAR;
|
|
306
|
-
let breakdownOfIType;
|
|
307
|
-
const propType = prop.mapToPk
|
|
308
|
-
? (() => {
|
|
309
|
-
const runtimeTypes = prop.columnTypes.map((t, i) => (prop.customTypes?.[i] ?? this.platform.getMappedType(t)).runtimeType);
|
|
310
|
-
return runtimeTypes.length === 1 ? runtimeTypes[0] : this.serializeObject(runtimeTypes);
|
|
311
|
-
})()
|
|
312
|
-
: (() => {
|
|
313
|
-
if (isScalar) {
|
|
314
|
-
if (prop.enum) {
|
|
315
|
-
const method = enumMode === 'ts-enum' ? 'getEnumClassName' : 'getEnumTypeName';
|
|
316
|
-
if (prop.nativeEnumName) {
|
|
317
|
-
return this.namingStrategy[method](prop.nativeEnumName, undefined, this.meta.schema);
|
|
318
|
-
}
|
|
319
|
-
return this.namingStrategy[method](prop.fieldNames[0], this.meta.collection, this.meta.schema);
|
|
320
|
-
}
|
|
321
|
-
breakdownOfIType = this.breakdownOfIType(prop);
|
|
322
|
-
if (typeof breakdownOfIType !== 'undefined') {
|
|
323
|
-
if (breakdownOfIType.length >= 3) {
|
|
324
|
-
hiddenType = '';
|
|
325
|
-
}
|
|
326
|
-
return `${this.referenceCoreImport('IType')}<${breakdownOfIType.join(', ')}>`;
|
|
327
|
-
}
|
|
328
|
-
return prop.runtimeType;
|
|
329
|
-
}
|
|
330
|
-
return prop.type;
|
|
331
|
-
})();
|
|
332
|
-
const hasUsableNullDefault = prop.nullable && !this.options.forceUndefined && prop.default === null;
|
|
333
|
-
const useDefault = hasUsableNullDefault ||
|
|
334
|
-
(!(typeof prop.default === 'undefined' || prop.default === null) &&
|
|
335
|
-
propType !== 'unknown' &&
|
|
336
|
-
typeof breakdownOfIType === 'undefined');
|
|
337
|
-
const optional = prop.nullable && (this.options.forceUndefined || prop.optional) ? '?' : useDefault ? '' : '!';
|
|
338
|
-
let ret = `${propName}${optional}: `;
|
|
339
|
-
const isArray = prop.array && (prop.kind === ReferenceKind.EMBEDDED || prop.enum);
|
|
340
|
-
const complexType = isArray ? `${propType}[]` : propType;
|
|
341
|
-
let wrappedType = prop.ref
|
|
342
|
-
? `${this.referenceCoreImport('Ref')}<${complexType}${isScalar && prop.nullable && !this.options.forceUndefined ? ' | null' : ''}>`
|
|
343
|
-
: this.options.esmImport && !isScalar
|
|
344
|
-
? `${this.referenceCoreImport('Rel')}<${complexType}>`
|
|
345
|
-
: complexType;
|
|
346
|
-
if (prop.nullable &&
|
|
347
|
-
!this.options.forceUndefined &&
|
|
348
|
-
(!isScalar || (!prop.ref && !wrappedType.includes(' | null')))) {
|
|
349
|
-
wrappedType += ' | null';
|
|
350
|
-
}
|
|
351
|
-
const optionalType = optional !== '?' && prop.optional ? ` & ${this.referenceCoreImport('Opt')}` : '';
|
|
352
|
-
ret +=
|
|
353
|
-
!this.options.forceUndefined && prop.nullable && (hiddenType || optionalType) ? `(${wrappedType})` : wrappedType;
|
|
354
|
-
ret += hiddenType;
|
|
355
|
-
ret += optionalType;
|
|
356
|
-
if (!useDefault) {
|
|
357
|
-
return `${padding}${ret};\n`;
|
|
358
|
-
}
|
|
359
|
-
if (prop.enum && typeof prop.default === 'string') {
|
|
360
|
-
if (enumMode === 'union-type') {
|
|
361
|
-
return `${padding}${ret} = ${this.quote(prop.default)};\n`;
|
|
362
|
-
}
|
|
363
|
-
const enumClassName = prop.nativeEnumName
|
|
364
|
-
? this.namingStrategy.getEnumClassName(prop.nativeEnumName, undefined, this.meta.schema)
|
|
365
|
-
: this.namingStrategy.getEnumClassName(prop.fieldNames[0], this.meta.collection, this.meta.schema);
|
|
366
|
-
const enumVal = this.namingStrategy.enumValueToEnumProperty(prop.default, prop.fieldNames[0], this.meta.collection, this.meta.schema);
|
|
367
|
-
return `${padding}${ret} = ${enumClassName}${identifierRegex.test(enumVal) ? `.${enumVal}` : `[${this.quote(enumVal)}]`};\n`;
|
|
368
|
-
}
|
|
369
|
-
if (prop.fieldNames?.length > 1) {
|
|
370
|
-
// TODO: Composite FKs with default values require additions to default/defaultRaw that are not yet supported.
|
|
371
|
-
return `${padding}${ret};\n`;
|
|
372
|
-
}
|
|
373
|
-
const defaultVal = typeof prop.default === 'string' ? this.quote(prop.default) : prop.default;
|
|
374
|
-
if (isScalar) {
|
|
375
|
-
return `${padding}${ret} = ${prop.ref ? `${this.referenceCoreImport('ref')}(${defaultVal})` : defaultVal};\n`;
|
|
376
|
-
}
|
|
377
|
-
if (hasUsableNullDefault) {
|
|
378
|
-
return `${padding}${ret} = null;\n`;
|
|
379
|
-
}
|
|
380
|
-
return `${padding}${ret} = ${prop.ref ? this.referenceCoreImport('ref') : this.referenceCoreImport('rel')}(${propType}, ${defaultVal});\n`;
|
|
381
|
-
}
|
|
382
|
-
getEnumClassDefinition(prop, padLeft) {
|
|
383
|
-
const enumMode = this.options.enumMode;
|
|
384
|
-
if (prop.nativeEnumName) {
|
|
385
|
-
const imports = [];
|
|
386
|
-
if (enumMode !== 'union-type') {
|
|
387
|
-
imports.push(prop.runtimeType);
|
|
388
|
-
}
|
|
389
|
-
if (!this.options.inferEntityType && enumMode !== 'ts-enum') {
|
|
390
|
-
const enumTypeName = this.namingStrategy.getEnumTypeName(prop.nativeEnumName, undefined, this.meta.schema);
|
|
391
|
-
imports.push(enumTypeName);
|
|
392
|
-
}
|
|
393
|
-
this.enumImports.set(prop.runtimeType, imports);
|
|
394
|
-
return '';
|
|
395
|
-
}
|
|
396
|
-
const enumClassName = this.namingStrategy.getEnumClassName(prop.fieldNames[0], this.meta.collection, this.meta.schema);
|
|
397
|
-
const enumTypeName = this.namingStrategy.getEnumTypeName(prop.fieldNames[0], this.meta.collection, this.meta.schema);
|
|
398
|
-
const padding = ' '.repeat(padLeft);
|
|
399
|
-
const enumValues = prop.items;
|
|
400
|
-
if (enumMode === 'union-type') {
|
|
401
|
-
return `export type ${enumTypeName} = ${enumValues.map(item => this.quote(item)).join(' | ')};\n`;
|
|
402
|
-
}
|
|
403
|
-
let ret = '';
|
|
404
|
-
if (enumMode === 'dictionary') {
|
|
405
|
-
ret += `export const ${enumClassName} = {\n`;
|
|
406
|
-
}
|
|
407
|
-
else {
|
|
408
|
-
ret += `export enum ${enumClassName} {\n`;
|
|
409
|
-
}
|
|
410
|
-
for (const enumValue of enumValues) {
|
|
411
|
-
const enumName = this.namingStrategy.enumValueToEnumProperty(enumValue, prop.fieldNames[0], this.meta.collection, this.meta.schema);
|
|
412
|
-
if (enumMode === 'dictionary') {
|
|
413
|
-
ret += `${padding}${identifierRegex.test(enumName) ? enumName : this.quote(enumName)}: ${this.quote(enumValue)},\n`;
|
|
414
|
-
}
|
|
415
|
-
else {
|
|
416
|
-
ret += `${padding}${identifierRegex.test(enumName) ? enumName : this.quote(enumName)} = ${this.quote(enumValue)},\n`;
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
if (enumMode === 'dictionary') {
|
|
420
|
-
ret += '} as const;\n';
|
|
421
|
-
}
|
|
422
|
-
else {
|
|
423
|
-
ret += '}\n';
|
|
424
|
-
}
|
|
425
|
-
if (enumMode === 'dictionary') {
|
|
426
|
-
ret += `\nexport type ${enumTypeName} = (typeof ${enumClassName})[keyof typeof ${enumClassName}];\n`;
|
|
427
|
-
}
|
|
428
|
-
return ret;
|
|
56
|
+
let classHead = '';
|
|
57
|
+
if (this.meta.className === this.options.customBaseEntityName) {
|
|
58
|
+
const defineConfigTypeSettings = {};
|
|
59
|
+
defineConfigTypeSettings.forceObject = this.platform.getConfig().get('serialization').forceObject ?? false;
|
|
60
|
+
classHead += `\n${' '.repeat(2)}[${this.referenceCoreImport('Config')}]?: ${this.referenceCoreImport('DefineConfig')}<${this.serializeObject(defineConfigTypeSettings)}>;\n\n`;
|
|
429
61
|
}
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
if (res.length <= wordwrap) {
|
|
434
|
-
return res;
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
const nextWordwrap = typeof wordwrap === 'number' ? 80 - (spaces ?? 0) - level * 2 : undefined;
|
|
438
|
-
const sep = typeof spaces === 'undefined' ? ', ' : `,\n${' '.repeat(spaces)}`;
|
|
439
|
-
const doIndent = typeof spaces !== 'undefined';
|
|
440
|
-
if (Array.isArray(options)) {
|
|
441
|
-
return `[${doIndent ? `\n${' '.repeat(spaces)}` : ''}${options.map(val => `${doIndent ? ' '.repeat(level * 2 + (spaces + 2)) : ''}${this.serializeValue(val, typeof nextWordwrap === 'number' ? nextWordwrap : undefined, doIndent ? spaces : undefined, level + 1)}`).join(sep)}${doIndent ? `${options.length > 0 ? ',\n' : ''}${' '.repeat(spaces + level * 2)}` : ''}]`;
|
|
442
|
-
}
|
|
443
|
-
const entries = Object.entries(options);
|
|
444
|
-
return `{${doIndent ? `\n${' '.repeat(spaces)}` : ' '}${entries
|
|
445
|
-
.map(([opt, val]) => {
|
|
446
|
-
const key = identifierRegex.test(opt) ? opt : this.quote(opt);
|
|
447
|
-
return `${doIndent ? ' '.repeat(level * 2 + (spaces + 2)) : ''}${key}: ${this.serializeValue(val, typeof nextWordwrap === 'number' ? nextWordwrap - key.length - 2 /* ': '.length*/ : undefined, doIndent ? spaces : undefined, level + 1)}`;
|
|
448
|
-
})
|
|
449
|
-
.join(sep)}${doIndent ? `${entries.length > 0 ? ',\n' : ''}${' '.repeat(spaces + level * 2)}` : ' '}}`;
|
|
450
|
-
}
|
|
451
|
-
serializeValue(val, wordwrap, spaces, level = 1) {
|
|
452
|
-
if (typeof val === 'object' && val !== null) {
|
|
453
|
-
return this.serializeObject(val, wordwrap, spaces, level);
|
|
454
|
-
}
|
|
455
|
-
return val;
|
|
62
|
+
if (this.meta.repositoryClass) {
|
|
63
|
+
this.entityImports.add(this.meta.repositoryClass);
|
|
64
|
+
classHead += `\n${' '.repeat(2)}[${this.referenceCoreImport('EntityRepositoryType')}]?: ${this.meta.repositoryClass};\n`;
|
|
456
65
|
}
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
const options = {};
|
|
488
|
-
const result = this.getCollectionDecl(options);
|
|
489
|
-
if (result.discriminatorColumn) {
|
|
490
|
-
result.discriminator = result.discriminatorColumn;
|
|
491
|
-
delete result.discriminatorColumn;
|
|
492
|
-
}
|
|
493
|
-
return result;
|
|
66
|
+
const enumDefinitions = [];
|
|
67
|
+
const eagerProperties = [];
|
|
68
|
+
const primaryProps = [];
|
|
69
|
+
let classBody = '';
|
|
70
|
+
Object.values(this.meta.properties).forEach(prop => {
|
|
71
|
+
const decorator = this.getPropertyDecorator(prop, 2);
|
|
72
|
+
const definition = this.getPropertyDefinition(prop, 2);
|
|
73
|
+
classBody += decorator;
|
|
74
|
+
classBody += definition;
|
|
75
|
+
classBody += '\n';
|
|
76
|
+
if (prop.enum) {
|
|
77
|
+
const def = this.getEnumClassDefinition(prop, 2);
|
|
78
|
+
if (def.length) {
|
|
79
|
+
enumDefinitions.push(def);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (prop.eager) {
|
|
83
|
+
eagerProperties.push(prop);
|
|
84
|
+
}
|
|
85
|
+
if (prop.primary && (!['id', '_id', 'uuid'].includes(prop.name) || this.meta.compositePK)) {
|
|
86
|
+
primaryProps.push(prop);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
if (primaryProps.length > 0) {
|
|
90
|
+
const primaryPropNames = primaryProps.map(prop => `'${prop.name}'`);
|
|
91
|
+
if (primaryProps.length > 1) {
|
|
92
|
+
classHead += `\n${' '.repeat(2)}[${this.referenceCoreImport('PrimaryKeyProp')}]?: [${primaryPropNames.join(', ')}];\n`;
|
|
93
|
+
} else {
|
|
94
|
+
classHead += `\n${' '.repeat(2)}[${this.referenceCoreImport('PrimaryKeyProp')}]?: ${primaryPropNames[0]};\n`;
|
|
95
|
+
}
|
|
494
96
|
}
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
}
|
|
499
|
-
if (this.meta.discriminatorValue) {
|
|
500
|
-
options.discriminatorValue =
|
|
501
|
-
typeof this.meta.discriminatorValue === 'string'
|
|
502
|
-
? this.quote(this.meta.discriminatorValue)
|
|
503
|
-
: this.meta.discriminatorValue;
|
|
504
|
-
}
|
|
505
|
-
if (this.meta.discriminatorColumn) {
|
|
506
|
-
options.discriminatorColumn = this.quote(this.meta.discriminatorColumn);
|
|
507
|
-
}
|
|
508
|
-
if (this.meta.discriminatorMap) {
|
|
509
|
-
options.discriminatorMap = Object.fromEntries(Object.entries(this.meta.discriminatorMap).map(([discriminatorValue, cls]) => [
|
|
510
|
-
discriminatorValue,
|
|
511
|
-
this.quote(Utils.className(cls)),
|
|
512
|
-
]));
|
|
513
|
-
}
|
|
514
|
-
return options;
|
|
515
|
-
}
|
|
516
|
-
getPropertyDecorator(prop, padLeft) {
|
|
517
|
-
const padding = ' '.repeat(padLeft);
|
|
518
|
-
const options = {};
|
|
519
|
-
let decorator = `@${this.referenceDecoratorImport(this.getDecoratorType(prop))}`;
|
|
520
|
-
if (prop.kind === ReferenceKind.MANY_TO_MANY) {
|
|
521
|
-
this.getManyToManyDecoratorOptions(options, prop);
|
|
522
|
-
}
|
|
523
|
-
else if (prop.kind === ReferenceKind.ONE_TO_MANY) {
|
|
524
|
-
this.getOneToManyDecoratorOptions(options, prop);
|
|
525
|
-
}
|
|
526
|
-
else if (prop.kind === ReferenceKind.SCALAR || typeof prop.kind === 'undefined') {
|
|
527
|
-
this.getScalarPropertyDecoratorOptions(options, prop);
|
|
528
|
-
}
|
|
529
|
-
else if (prop.kind === ReferenceKind.EMBEDDED) {
|
|
530
|
-
this.getEmbeddedPropertyDeclarationOptions(options, prop);
|
|
531
|
-
}
|
|
532
|
-
else {
|
|
533
|
-
this.getForeignKeyDecoratorOptions(options, prop);
|
|
534
|
-
}
|
|
535
|
-
this.getCommonDecoratorOptions(options, prop);
|
|
536
|
-
const indexes = this.getPropertyIndexes(prop, options);
|
|
537
|
-
decorator = [...indexes.sort(), decorator].map(d => padding + d).join('\n');
|
|
538
|
-
const decoratorArgs = [];
|
|
539
|
-
if (prop.formula) {
|
|
540
|
-
decoratorArgs.push(prop.formula.toString());
|
|
541
|
-
}
|
|
542
|
-
if (Utils.hasObjectKeys(options)) {
|
|
543
|
-
decoratorArgs.push(this.serializeObject(options));
|
|
544
|
-
}
|
|
545
|
-
return `${decorator}(${decoratorArgs.join(', ')})\n`;
|
|
97
|
+
if (eagerProperties.length > 0) {
|
|
98
|
+
const eagerPropertyNames = eagerProperties.map(prop => `'${prop.name}'`).sort();
|
|
99
|
+
classHead += `\n${' '.repeat(2)}[${this.referenceCoreImport('EagerProps')}]?: ${eagerPropertyNames.join(' | ')};\n`;
|
|
546
100
|
}
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
101
|
+
ret += this.getEntityClass(classBody ? `${classHead}\n${classBody}` : classHead);
|
|
102
|
+
ret = `${this.generateImports()}\n\n${enumDefinitions.length ? enumDefinitions.join('\n') + '\n' : ''}${ret}`;
|
|
103
|
+
return ret;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Convert index column options to quoted output format.
|
|
107
|
+
*/
|
|
108
|
+
getColumnOptions(columns) {
|
|
109
|
+
if (!columns?.length) {
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
return columns.map(col => {
|
|
113
|
+
const colOpt = { name: this.quote(col.name) };
|
|
114
|
+
if (col.sort) {
|
|
115
|
+
colOpt.sort = this.quote(col.sort.toUpperCase());
|
|
116
|
+
}
|
|
117
|
+
if (col.nulls) {
|
|
118
|
+
colOpt.nulls = this.quote(col.nulls.toUpperCase());
|
|
119
|
+
}
|
|
120
|
+
if (col.length != null) {
|
|
121
|
+
colOpt.length = col.length;
|
|
122
|
+
}
|
|
123
|
+
if (col.collation) {
|
|
124
|
+
colOpt.collation = this.quote(col.collation);
|
|
125
|
+
}
|
|
126
|
+
return colOpt;
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
getIndexOptions(index, isAtEntityLevel = true) {
|
|
130
|
+
const indexOpt = {};
|
|
131
|
+
if (typeof index.name === 'string') {
|
|
132
|
+
indexOpt.name = this.quote(index.name);
|
|
133
|
+
}
|
|
134
|
+
if (typeof index.expression === 'string') {
|
|
135
|
+
indexOpt.expression = this.quote(index.expression);
|
|
136
|
+
} else if (typeof index.expression === 'function') {
|
|
137
|
+
indexOpt.expression = `${index.expression}`.replace(')=>`', ') => `');
|
|
138
|
+
}
|
|
139
|
+
if (isAtEntityLevel && index.properties) {
|
|
140
|
+
indexOpt.properties = Utils.asArray(index.properties).map(prop => this.quote('' + prop));
|
|
141
|
+
}
|
|
142
|
+
// Index type (e.g., 'fulltext', 'spatial', 'btree', 'hash')
|
|
143
|
+
if (index.type) {
|
|
144
|
+
indexOpt.type = this.quote(index.type);
|
|
145
|
+
}
|
|
146
|
+
// Advanced index options
|
|
147
|
+
const columns = this.getColumnOptions(index.columns);
|
|
148
|
+
if (columns) {
|
|
149
|
+
indexOpt.columns = columns;
|
|
150
|
+
}
|
|
151
|
+
if (index.include) {
|
|
152
|
+
indexOpt.include = Utils.asArray(index.include).map(prop => this.quote('' + prop));
|
|
153
|
+
}
|
|
154
|
+
if (index.fillFactor != null) {
|
|
155
|
+
indexOpt.fillFactor = index.fillFactor;
|
|
156
|
+
}
|
|
157
|
+
if (index.invisible) {
|
|
158
|
+
indexOpt.invisible = true;
|
|
159
|
+
}
|
|
160
|
+
if (index.disabled) {
|
|
161
|
+
indexOpt.disabled = true;
|
|
162
|
+
}
|
|
163
|
+
if (index.clustered) {
|
|
164
|
+
indexOpt.clustered = true;
|
|
165
|
+
}
|
|
166
|
+
return indexOpt;
|
|
167
|
+
}
|
|
168
|
+
getUniqueOptions(index, isAtEntityLevel = true) {
|
|
169
|
+
const uniqueOpt = {};
|
|
170
|
+
if (typeof index.name === 'string') {
|
|
171
|
+
uniqueOpt.name = this.quote(index.name);
|
|
172
|
+
}
|
|
173
|
+
if (typeof index.expression === 'string') {
|
|
174
|
+
uniqueOpt.expression = this.quote(index.expression);
|
|
175
|
+
} else if (typeof index.expression === 'function') {
|
|
176
|
+
uniqueOpt.expression = `${index.expression}`.replace(')=>`', ') => `');
|
|
177
|
+
}
|
|
178
|
+
if (isAtEntityLevel && index.properties) {
|
|
179
|
+
uniqueOpt.properties = Utils.asArray(index.properties).map(prop => this.quote('' + prop));
|
|
180
|
+
}
|
|
181
|
+
if (index.deferMode) {
|
|
182
|
+
uniqueOpt.deferMode = `${this.referenceCoreImport('DeferMode')}.INITIALLY_${index.deferMode.toUpperCase()}`;
|
|
183
|
+
}
|
|
184
|
+
const columns = this.getColumnOptions(index.columns);
|
|
185
|
+
if (columns) {
|
|
186
|
+
uniqueOpt.columns = columns;
|
|
187
|
+
}
|
|
188
|
+
if (index.include) {
|
|
189
|
+
uniqueOpt.include = Utils.asArray(index.include).map(prop => this.quote('' + prop));
|
|
190
|
+
}
|
|
191
|
+
if (index.fillFactor != null) {
|
|
192
|
+
uniqueOpt.fillFactor = index.fillFactor;
|
|
193
|
+
}
|
|
194
|
+
if (index.disabled) {
|
|
195
|
+
uniqueOpt.disabled = true;
|
|
196
|
+
}
|
|
197
|
+
return uniqueOpt;
|
|
198
|
+
}
|
|
199
|
+
generateImports() {
|
|
200
|
+
const imports = new Set();
|
|
201
|
+
if (this.coreImports.size > 0) {
|
|
202
|
+
imports.add(
|
|
203
|
+
`import { ${[...this.coreImports]
|
|
204
|
+
.sort()
|
|
205
|
+
.map(t => {
|
|
206
|
+
let ret = POSSIBLE_TYPE_IMPORTS.includes(t) ? `type ${t}` : t;
|
|
207
|
+
if (this.options.coreImportsPrefix) {
|
|
208
|
+
const resolvedIdentifier = `${this.options.coreImportsPrefix}${t}`;
|
|
209
|
+
ret += ` as ${resolvedIdentifier}`;
|
|
210
|
+
}
|
|
211
|
+
return ret;
|
|
212
|
+
})
|
|
213
|
+
.join(', ')} } from '@mikro-orm/core';`,
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
if (this.decoratorImports.size > 0) {
|
|
217
|
+
const type = this.options.decorators;
|
|
218
|
+
imports.add(
|
|
219
|
+
`import { ${[...this.decoratorImports]
|
|
220
|
+
.sort()
|
|
221
|
+
.map(t => {
|
|
222
|
+
let ret = t;
|
|
223
|
+
if (this.options.coreImportsPrefix) {
|
|
224
|
+
const resolvedIdentifier = `${this.options.coreImportsPrefix}${t}`;
|
|
225
|
+
ret += ` as ${resolvedIdentifier}`;
|
|
226
|
+
}
|
|
227
|
+
return ret;
|
|
228
|
+
})
|
|
229
|
+
.join(', ')} } from '@mikro-orm/decorators/${type}';`,
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
const extension = this.options.esmImport ? '.js' : '';
|
|
233
|
+
const { dir, base } = parse(`${this.options.path ?? '.'}/${this.getBaseName()}`);
|
|
234
|
+
const basePath = relative(dir, this.options.path ?? '.') || '.';
|
|
235
|
+
(this.options.extraImports?.(basePath, base) ?? []).forEach(v => this.entityImports.add(v));
|
|
236
|
+
const entityImports = [...this.entityImports].filter(e => e !== this.meta.className);
|
|
237
|
+
const importMap = new Map();
|
|
238
|
+
for (const entity of entityImports) {
|
|
239
|
+
const file = this.options.onImport?.(entity, basePath, extension, base) ?? {
|
|
240
|
+
path: `${basePath}/${this.options.fileName(entity)}${extension}`,
|
|
241
|
+
name: entity,
|
|
242
|
+
};
|
|
243
|
+
if (file.path === '') {
|
|
244
|
+
if (file.name === '') {
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
importMap.set(file.path, `import ${this.quote(file.name)};`);
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
if (file.name === '') {
|
|
251
|
+
importMap.set(file.path, `import * as ${entity} from ${this.quote(file.path)};`);
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
if (file.name === 'default') {
|
|
255
|
+
importMap.set(file.path, `import ${entity} from ${this.quote(file.path)};`);
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
if (file.name === entity) {
|
|
259
|
+
importMap.set(file.path, `import { ${entity} } from ${this.quote(file.path)};`);
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
importMap.set(
|
|
263
|
+
file.path,
|
|
264
|
+
`import { ${identifierRegex.test(file.name) ? file.name : this.quote(file.name)} as ${entity} } from ${this.quote(file.path)};`,
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
if (this.enumImports.size) {
|
|
268
|
+
for (const [name, exports] of this.enumImports.entries()) {
|
|
269
|
+
const file = this.options.onImport?.(name, basePath, extension, base) ?? {
|
|
270
|
+
path: `${basePath}/${this.options.fileName(name)}${extension}`,
|
|
271
|
+
name,
|
|
562
272
|
};
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
const ret = [];
|
|
566
|
-
let propIndexIsNonTrivialIndex = false;
|
|
567
|
-
const nonTrivialIndexes = this.meta.indexes.filter(i => i.properties?.length === 1 && i.properties[0] === prop.name);
|
|
568
|
-
for (const i of nonTrivialIndexes) {
|
|
569
|
-
ret.push(`@${this.referenceDecoratorImport('Index')}(${this.serializeObject(this.getIndexOptions(i, false))})`);
|
|
570
|
-
if (prop.index === i.name) {
|
|
571
|
-
propIndexIsNonTrivialIndex = true;
|
|
572
|
-
delete options.index;
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
if (prop.index && !options.index && !propIndexIsNonTrivialIndex) {
|
|
576
|
-
ret.push(`@${this.referenceDecoratorImport('Index')}(${typeof prop.index === 'string' ? `{ name: ${this.quote(prop.index)} }` : ''})`);
|
|
577
|
-
}
|
|
578
|
-
let propIndexIsNonTrivialUnique = false;
|
|
579
|
-
const nonTrivialUnique = this.meta.uniques.filter(i => i.properties?.length === 1 && i.properties[0] === prop.name);
|
|
580
|
-
for (const i of nonTrivialUnique) {
|
|
581
|
-
ret.push(`@${this.referenceDecoratorImport('Unique')}(${this.serializeObject(this.getUniqueOptions(i, false))})`);
|
|
582
|
-
if (prop.unique === i.name) {
|
|
583
|
-
propIndexIsNonTrivialUnique = true;
|
|
584
|
-
delete options.unique;
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
if (prop.unique && !options.unique && !propIndexIsNonTrivialUnique) {
|
|
588
|
-
ret.push(`@${this.referenceDecoratorImport('Unique')}(${typeof prop.unique === 'string' ? `{ name: ${this.quote(prop.unique)} }` : ''})`);
|
|
589
|
-
}
|
|
590
|
-
return ret;
|
|
273
|
+
importMap.set(file.path, `import { ${exports.join(', ')} } from ${this.quote(file.path)};`);
|
|
274
|
+
}
|
|
591
275
|
}
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
options.nullable = true;
|
|
595
|
-
}
|
|
596
|
-
if (prop.primary && (prop.enum || !(typeof prop.kind === 'undefined' || prop.kind === ReferenceKind.SCALAR))) {
|
|
597
|
-
options.primary = true;
|
|
598
|
-
}
|
|
599
|
-
['persist', 'hydrate'].filter(key => prop[key] === false).forEach(key => (options[key] = false));
|
|
600
|
-
['onCreate', 'onUpdate', 'serializer']
|
|
601
|
-
.filter(key => typeof prop[key] === 'function')
|
|
602
|
-
.forEach(key => (options[key] = `${prop[key]}`));
|
|
603
|
-
if (typeof prop.serializedName === 'string') {
|
|
604
|
-
options.serializedName = this.quote(prop.serializedName);
|
|
605
|
-
}
|
|
606
|
-
if (Array.isArray(prop.groups)) {
|
|
607
|
-
options.groups = prop.groups.map(group => this.quote(group));
|
|
608
|
-
}
|
|
609
|
-
['hidden', 'version', 'concurrencyCheck', 'eager', 'lazy', 'orphanRemoval']
|
|
610
|
-
.filter(key => prop[key])
|
|
611
|
-
.forEach(key => (options[key] = true));
|
|
612
|
-
if (prop.cascade && (prop.cascade.length !== 1 || prop.cascade[0] !== Cascade.PERSIST)) {
|
|
613
|
-
options.cascade = `[${prop.cascade.map(value => `${this.referenceCoreImport('Cascade')}.${value.toUpperCase()}`).join(', ')}]`;
|
|
614
|
-
}
|
|
615
|
-
if (typeof prop.comment === 'string') {
|
|
616
|
-
options.comment = this.quote(prop.comment);
|
|
617
|
-
}
|
|
618
|
-
// TODO: Composite FKs with default values require additions to default/defaultRaw that are not yet supported.
|
|
619
|
-
if (prop.fieldNames?.length <= 1) {
|
|
620
|
-
if (typeof prop.defaultRaw !== 'undefined' &&
|
|
621
|
-
prop.defaultRaw !== 'null' &&
|
|
622
|
-
prop.defaultRaw !== '' &&
|
|
623
|
-
prop.defaultRaw !== (typeof prop.default === 'string' ? this.quote(prop.default) : `${prop.default}`)) {
|
|
624
|
-
options.defaultRaw = `\`${prop.defaultRaw}\``;
|
|
625
|
-
}
|
|
626
|
-
else if (!(typeof prop.default === 'undefined' || prop.default === null) &&
|
|
627
|
-
(prop.ref ||
|
|
628
|
-
(!prop.enum &&
|
|
629
|
-
(typeof prop.kind === 'undefined' || prop.kind === ReferenceKind.SCALAR) &&
|
|
630
|
-
(prop.type === 'unknown' || typeof this.breakdownOfIType(prop) !== 'undefined')))) {
|
|
631
|
-
options.default = typeof prop.default === 'string' ? this.quote(prop.default) : prop.default;
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
if (typeof prop.extra === 'string') {
|
|
635
|
-
options.extra = this.quote(prop.extra);
|
|
636
|
-
}
|
|
637
|
-
if (prop.deferMode) {
|
|
638
|
-
options.deferMode = `${this.referenceCoreImport('DeferMode')}.INITIALLY_${prop.deferMode.toUpperCase()}`;
|
|
639
|
-
}
|
|
640
|
-
if (typeof prop.ignoreSchemaChanges !== 'undefined') {
|
|
641
|
-
options.ignoreSchemaChanges ??= [];
|
|
642
|
-
options.ignoreSchemaChanges.push(...prop.ignoreSchemaChanges.map(v => this.quote(v)));
|
|
643
|
-
}
|
|
276
|
+
for (const key of [...importMap.keys()].sort()) {
|
|
277
|
+
imports.add(importMap.get(key));
|
|
644
278
|
}
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
279
|
+
return Array.from(imports.values()).join('\n');
|
|
280
|
+
}
|
|
281
|
+
getEntityClass(classBody) {
|
|
282
|
+
let ret = `export `;
|
|
283
|
+
if (this.meta.abstract) {
|
|
284
|
+
ret += `abstract `;
|
|
285
|
+
}
|
|
286
|
+
ret += `class ${this.meta.className}`;
|
|
287
|
+
if (this.meta.extends) {
|
|
288
|
+
this.entityImports.add(Utils.className(this.meta.extends));
|
|
289
|
+
ret += ` extends ${Utils.className(this.meta.extends)}`;
|
|
290
|
+
} else if (this.options.useCoreBaseEntity) {
|
|
291
|
+
ret += ` extends ${this.referenceCoreImport('BaseEntity')}`;
|
|
292
|
+
}
|
|
293
|
+
ret += ` {\n${classBody}}\n`;
|
|
294
|
+
return ret;
|
|
295
|
+
}
|
|
296
|
+
getBaseName(extension = '.ts') {
|
|
297
|
+
return `${this.options.fileName(this.meta.className)}${extension}`;
|
|
298
|
+
}
|
|
299
|
+
quote(val) {
|
|
300
|
+
const backtick = val.startsWith(`'`) || val.includes('\n');
|
|
301
|
+
/* v8 ignore next */
|
|
302
|
+
return backtick ? `\`${val.replaceAll('`', '\\``')}\`` : `'${val.replaceAll(`'`, `\\'`)}'`;
|
|
303
|
+
}
|
|
304
|
+
getPropertyDefinition(prop, padLeft) {
|
|
305
|
+
const padding = ' '.repeat(padLeft);
|
|
306
|
+
const propName = identifierRegex.test(prop.name) ? prop.name : this.quote(prop.name);
|
|
307
|
+
const enumMode = this.options.enumMode;
|
|
308
|
+
let hiddenType = '';
|
|
309
|
+
if (prop.hidden) {
|
|
310
|
+
hiddenType += ` & ${this.referenceCoreImport('Hidden')}`;
|
|
311
|
+
}
|
|
312
|
+
if ([ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(prop.kind)) {
|
|
313
|
+
return `${padding}${propName}${hiddenType ? `: ${this.referenceCoreImport('Collection')}<${prop.type}>${hiddenType}` : ''} = new ${this.referenceCoreImport('Collection')}<${prop.type}>(this);\n`;
|
|
314
|
+
}
|
|
315
|
+
const isScalar = typeof prop.kind === 'undefined' || prop.kind === ReferenceKind.SCALAR;
|
|
316
|
+
let breakdownOfIType;
|
|
317
|
+
const propType = prop.mapToPk
|
|
318
|
+
? (() => {
|
|
319
|
+
const runtimeTypes = prop.columnTypes.map(
|
|
320
|
+
(t, i) => (prop.customTypes?.[i] ?? this.platform.getMappedType(t)).runtimeType,
|
|
321
|
+
);
|
|
322
|
+
return runtimeTypes.length === 1 ? runtimeTypes[0] : this.serializeObject(runtimeTypes);
|
|
323
|
+
})()
|
|
324
|
+
: (() => {
|
|
325
|
+
if (isScalar) {
|
|
326
|
+
if (prop.enum) {
|
|
327
|
+
const method = enumMode === 'ts-enum' ? 'getEnumClassName' : 'getEnumTypeName';
|
|
328
|
+
if (prop.nativeEnumName) {
|
|
329
|
+
return this.namingStrategy[method](prop.nativeEnumName, undefined, this.meta.schema);
|
|
330
|
+
}
|
|
331
|
+
return this.namingStrategy[method](prop.fieldNames[0], this.meta.collection, this.meta.schema);
|
|
332
|
+
}
|
|
333
|
+
breakdownOfIType = this.breakdownOfIType(prop);
|
|
334
|
+
if (typeof breakdownOfIType !== 'undefined') {
|
|
335
|
+
if (breakdownOfIType.length >= 3) {
|
|
336
|
+
hiddenType = '';
|
|
337
|
+
}
|
|
338
|
+
return `${this.referenceCoreImport('IType')}<${breakdownOfIType.join(', ')}>`;
|
|
339
|
+
}
|
|
340
|
+
return prop.runtimeType;
|
|
341
|
+
}
|
|
342
|
+
return prop.type;
|
|
343
|
+
})();
|
|
344
|
+
const hasUsableNullDefault = prop.nullable && !this.options.forceUndefined && prop.default === null;
|
|
345
|
+
const useDefault =
|
|
346
|
+
hasUsableNullDefault ||
|
|
347
|
+
(!(typeof prop.default === 'undefined' || prop.default === null) &&
|
|
348
|
+
propType !== 'unknown' &&
|
|
349
|
+
typeof breakdownOfIType === 'undefined');
|
|
350
|
+
const optional = prop.nullable && (this.options.forceUndefined || prop.optional) ? '?' : useDefault ? '' : '!';
|
|
351
|
+
let ret = `${propName}${optional}: `;
|
|
352
|
+
const isArray = prop.array && (prop.kind === ReferenceKind.EMBEDDED || prop.enum);
|
|
353
|
+
const complexType = isArray ? `${propType}[]` : propType;
|
|
354
|
+
let wrappedType = prop.ref
|
|
355
|
+
? `${this.referenceCoreImport('Ref')}<${complexType}${isScalar && prop.nullable && !this.options.forceUndefined ? ' | null' : ''}>`
|
|
356
|
+
: this.options.esmImport && !isScalar
|
|
357
|
+
? `${this.referenceCoreImport('Rel')}<${complexType}>`
|
|
358
|
+
: complexType;
|
|
359
|
+
if (
|
|
360
|
+
prop.nullable &&
|
|
361
|
+
!this.options.forceUndefined &&
|
|
362
|
+
(!isScalar || (!prop.ref && !wrappedType.includes(' | null')))
|
|
363
|
+
) {
|
|
364
|
+
wrappedType += ' | null';
|
|
365
|
+
}
|
|
366
|
+
const optionalType = optional !== '?' && prop.optional ? ` & ${this.referenceCoreImport('Opt')}` : '';
|
|
367
|
+
ret +=
|
|
368
|
+
!this.options.forceUndefined && prop.nullable && (hiddenType || optionalType) ? `(${wrappedType})` : wrappedType;
|
|
369
|
+
ret += hiddenType;
|
|
370
|
+
ret += optionalType;
|
|
371
|
+
if (!useDefault) {
|
|
372
|
+
return `${padding}${ret};\n`;
|
|
373
|
+
}
|
|
374
|
+
if (prop.enum && typeof prop.default === 'string') {
|
|
375
|
+
if (enumMode === 'union-type') {
|
|
376
|
+
return `${padding}${ret} = ${this.quote(prop.default)};\n`;
|
|
377
|
+
}
|
|
378
|
+
const enumClassName = prop.nativeEnumName
|
|
379
|
+
? this.namingStrategy.getEnumClassName(prop.nativeEnumName, undefined, this.meta.schema)
|
|
380
|
+
: this.namingStrategy.getEnumClassName(prop.fieldNames[0], this.meta.collection, this.meta.schema);
|
|
381
|
+
const enumVal = this.namingStrategy.enumValueToEnumProperty(
|
|
382
|
+
prop.default,
|
|
383
|
+
prop.fieldNames[0],
|
|
384
|
+
this.meta.collection,
|
|
385
|
+
this.meta.schema,
|
|
386
|
+
);
|
|
387
|
+
return `${padding}${ret} = ${enumClassName}${identifierRegex.test(enumVal) ? `.${enumVal}` : `[${this.quote(enumVal)}]`};\n`;
|
|
388
|
+
}
|
|
389
|
+
if (prop.fieldNames?.length > 1) {
|
|
390
|
+
// TODO: Composite FKs with default values require additions to default/defaultRaw that are not yet supported.
|
|
391
|
+
return `${padding}${ret};\n`;
|
|
392
|
+
}
|
|
393
|
+
const defaultVal = typeof prop.default === 'string' ? this.quote(prop.default) : prop.default;
|
|
394
|
+
if (isScalar) {
|
|
395
|
+
return `${padding}${ret} = ${prop.ref ? `${this.referenceCoreImport('ref')}(${defaultVal})` : defaultVal};\n`;
|
|
396
|
+
}
|
|
397
|
+
if (hasUsableNullDefault) {
|
|
398
|
+
return `${padding}${ret} = null;\n`;
|
|
399
|
+
}
|
|
400
|
+
return `${padding}${ret} = ${prop.ref ? this.referenceCoreImport('ref') : this.referenceCoreImport('rel')}(${propType}, ${defaultVal});\n`;
|
|
401
|
+
}
|
|
402
|
+
getEnumClassDefinition(prop, padLeft) {
|
|
403
|
+
const enumMode = this.options.enumMode;
|
|
404
|
+
if (prop.nativeEnumName) {
|
|
405
|
+
const imports = [];
|
|
406
|
+
if (enumMode !== 'union-type') {
|
|
407
|
+
imports.push(prop.runtimeType);
|
|
408
|
+
}
|
|
409
|
+
if (!this.options.inferEntityType && enumMode !== 'ts-enum') {
|
|
410
|
+
const enumTypeName = this.namingStrategy.getEnumTypeName(prop.nativeEnumName, undefined, this.meta.schema);
|
|
411
|
+
imports.push(enumTypeName);
|
|
412
|
+
}
|
|
413
|
+
this.enumImports.set(prop.runtimeType, imports);
|
|
414
|
+
return '';
|
|
415
|
+
}
|
|
416
|
+
const enumClassName = this.namingStrategy.getEnumClassName(
|
|
417
|
+
prop.fieldNames[0],
|
|
418
|
+
this.meta.collection,
|
|
419
|
+
this.meta.schema,
|
|
420
|
+
);
|
|
421
|
+
const enumTypeName = this.namingStrategy.getEnumTypeName(
|
|
422
|
+
prop.fieldNames[0],
|
|
423
|
+
this.meta.collection,
|
|
424
|
+
this.meta.schema,
|
|
425
|
+
);
|
|
426
|
+
const padding = ' '.repeat(padLeft);
|
|
427
|
+
const enumValues = prop.items;
|
|
428
|
+
if (enumMode === 'union-type') {
|
|
429
|
+
return `export type ${enumTypeName} = ${enumValues.map(item => this.quote(item)).join(' | ')};\n`;
|
|
430
|
+
}
|
|
431
|
+
let ret = '';
|
|
432
|
+
if (enumMode === 'dictionary') {
|
|
433
|
+
ret += `export const ${enumClassName} = {\n`;
|
|
434
|
+
} else {
|
|
435
|
+
ret += `export enum ${enumClassName} {\n`;
|
|
436
|
+
}
|
|
437
|
+
for (const enumValue of enumValues) {
|
|
438
|
+
const enumName = this.namingStrategy.enumValueToEnumProperty(
|
|
439
|
+
enumValue,
|
|
440
|
+
prop.fieldNames[0],
|
|
441
|
+
this.meta.collection,
|
|
442
|
+
this.meta.schema,
|
|
443
|
+
);
|
|
444
|
+
if (enumMode === 'dictionary') {
|
|
445
|
+
ret += `${padding}${identifierRegex.test(enumName) ? enumName : this.quote(enumName)}: ${this.quote(enumValue)},\n`;
|
|
446
|
+
} else {
|
|
447
|
+
ret += `${padding}${identifierRegex.test(enumName) ? enumName : this.quote(enumName)} = ${this.quote(enumValue)},\n`;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
if (enumMode === 'dictionary') {
|
|
451
|
+
ret += '} as const;\n';
|
|
452
|
+
} else {
|
|
453
|
+
ret += '}\n';
|
|
454
|
+
}
|
|
455
|
+
if (enumMode === 'dictionary') {
|
|
456
|
+
ret += `\nexport type ${enumTypeName} = (typeof ${enumClassName})[keyof typeof ${enumClassName}];\n`;
|
|
457
|
+
}
|
|
458
|
+
return ret;
|
|
459
|
+
}
|
|
460
|
+
serializeObject(options, wordwrap, spaces, level = 0) {
|
|
461
|
+
if (typeof wordwrap === 'number' && !Object.hasOwn(options, Config)) {
|
|
462
|
+
const res = this.serializeObject(options, undefined, undefined, level);
|
|
463
|
+
if (res.length <= wordwrap) {
|
|
464
|
+
return res;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
const nextWordwrap = typeof wordwrap === 'number' ? 80 - (spaces ?? 0) - level * 2 : undefined;
|
|
468
|
+
const sep = typeof spaces === 'undefined' ? ', ' : `,\n${' '.repeat(spaces)}`;
|
|
469
|
+
const doIndent = typeof spaces !== 'undefined';
|
|
470
|
+
if (Array.isArray(options)) {
|
|
471
|
+
return `[${doIndent ? `\n${' '.repeat(spaces)}` : ''}${options.map(val => `${doIndent ? ' '.repeat(level * 2 + (spaces + 2)) : ''}${this.serializeValue(val, typeof nextWordwrap === 'number' ? nextWordwrap : undefined, doIndent ? spaces : undefined, level + 1)}`).join(sep)}${doIndent ? `${options.length > 0 ? ',\n' : ''}${' '.repeat(spaces + level * 2)}` : ''}]`;
|
|
472
|
+
}
|
|
473
|
+
const entries = Object.entries(options);
|
|
474
|
+
return `{${doIndent ? `\n${' '.repeat(spaces)}` : ' '}${entries
|
|
475
|
+
.map(([opt, val]) => {
|
|
476
|
+
const key = identifierRegex.test(opt) ? opt : this.quote(opt);
|
|
477
|
+
return `${doIndent ? ' '.repeat(level * 2 + (spaces + 2)) : ''}${key}: ${this.serializeValue(val, typeof nextWordwrap === 'number' ? nextWordwrap - key.length - 2 /* ': '.length*/ : undefined, doIndent ? spaces : undefined, level + 1)}`;
|
|
478
|
+
})
|
|
479
|
+
.join(sep)}${doIndent ? `${entries.length > 0 ? ',\n' : ''}${' '.repeat(spaces + level * 2)}` : ' '}}`;
|
|
480
|
+
}
|
|
481
|
+
serializeValue(val, wordwrap, spaces, level = 1) {
|
|
482
|
+
if (typeof val === 'object' && val !== null) {
|
|
483
|
+
return this.serializeObject(val, wordwrap, spaces, level);
|
|
484
|
+
}
|
|
485
|
+
return val;
|
|
486
|
+
}
|
|
487
|
+
getEntityDeclOptions() {
|
|
488
|
+
const options = {};
|
|
489
|
+
if (this.meta.tableName !== this.namingStrategy.classToTableName(this.meta.className)) {
|
|
490
|
+
options.tableName = this.quote(this.meta.tableName);
|
|
491
|
+
}
|
|
492
|
+
if (this.meta.schema && this.meta.schema !== this.platform.getDefaultSchemaName()) {
|
|
493
|
+
options.schema = this.quote(this.meta.schema);
|
|
494
|
+
}
|
|
495
|
+
if (typeof this.meta.expression === 'string') {
|
|
496
|
+
options.expression = this.quote(this.meta.expression);
|
|
497
|
+
} else if (typeof this.meta.expression === 'function') {
|
|
498
|
+
options.expression = this.meta.expression.toString();
|
|
499
|
+
}
|
|
500
|
+
if (this.meta.repositoryClass) {
|
|
501
|
+
this.entityImports.add(this.meta.repositoryClass);
|
|
502
|
+
options.repository = `() => ${this.meta.repositoryClass}`;
|
|
503
|
+
}
|
|
504
|
+
if (this.meta.comment) {
|
|
505
|
+
options.comment = this.quote(this.meta.comment);
|
|
506
|
+
}
|
|
507
|
+
if (this.meta.readonly && !this.meta.virtual) {
|
|
508
|
+
options.readonly = this.meta.readonly;
|
|
509
|
+
}
|
|
510
|
+
if (this.meta.virtual) {
|
|
511
|
+
options.virtual = this.meta.virtual;
|
|
512
|
+
}
|
|
513
|
+
return this.getCollectionDecl(options);
|
|
514
|
+
}
|
|
515
|
+
getEmbeddableDeclOptions() {
|
|
516
|
+
const options = {};
|
|
517
|
+
const result = this.getCollectionDecl(options);
|
|
518
|
+
if (result.discriminatorColumn) {
|
|
519
|
+
result.discriminator = result.discriminatorColumn;
|
|
520
|
+
delete result.discriminatorColumn;
|
|
521
|
+
}
|
|
522
|
+
return result;
|
|
523
|
+
}
|
|
524
|
+
getCollectionDecl(options) {
|
|
525
|
+
if (this.meta.abstract) {
|
|
526
|
+
options.abstract = true;
|
|
527
|
+
}
|
|
528
|
+
if (this.meta.discriminatorValue) {
|
|
529
|
+
options.discriminatorValue =
|
|
530
|
+
typeof this.meta.discriminatorValue === 'string'
|
|
531
|
+
? this.quote(this.meta.discriminatorValue)
|
|
532
|
+
: this.meta.discriminatorValue;
|
|
533
|
+
}
|
|
534
|
+
if (this.meta.discriminatorColumn) {
|
|
535
|
+
options.discriminatorColumn = this.quote(this.meta.discriminatorColumn);
|
|
536
|
+
}
|
|
537
|
+
if (this.meta.discriminatorMap) {
|
|
538
|
+
options.discriminatorMap = Object.fromEntries(
|
|
539
|
+
Object.entries(this.meta.discriminatorMap).map(([discriminatorValue, cls]) => [
|
|
540
|
+
discriminatorValue,
|
|
541
|
+
this.quote(Utils.className(cls)),
|
|
542
|
+
]),
|
|
543
|
+
);
|
|
544
|
+
}
|
|
545
|
+
return options;
|
|
546
|
+
}
|
|
547
|
+
getPropertyDecorator(prop, padLeft) {
|
|
548
|
+
const padding = ' '.repeat(padLeft);
|
|
549
|
+
const options = {};
|
|
550
|
+
let decorator = `@${this.referenceDecoratorImport(this.getDecoratorType(prop))}`;
|
|
551
|
+
if (prop.kind === ReferenceKind.MANY_TO_MANY) {
|
|
552
|
+
this.getManyToManyDecoratorOptions(options, prop);
|
|
553
|
+
} else if (prop.kind === ReferenceKind.ONE_TO_MANY) {
|
|
554
|
+
this.getOneToManyDecoratorOptions(options, prop);
|
|
555
|
+
} else if (prop.kind === ReferenceKind.SCALAR || typeof prop.kind === 'undefined') {
|
|
556
|
+
this.getScalarPropertyDecoratorOptions(options, prop);
|
|
557
|
+
} else if (prop.kind === ReferenceKind.EMBEDDED) {
|
|
558
|
+
this.getEmbeddedPropertyDeclarationOptions(options, prop);
|
|
559
|
+
} else {
|
|
560
|
+
this.getForeignKeyDecoratorOptions(options, prop);
|
|
561
|
+
}
|
|
562
|
+
this.getCommonDecoratorOptions(options, prop);
|
|
563
|
+
const indexes = this.getPropertyIndexes(prop, options);
|
|
564
|
+
decorator = [...indexes.sort(), decorator].map(d => padding + d).join('\n');
|
|
565
|
+
const decoratorArgs = [];
|
|
566
|
+
if (prop.formula) {
|
|
567
|
+
decoratorArgs.push(prop.formula.toString());
|
|
568
|
+
}
|
|
569
|
+
if (Utils.hasObjectKeys(options)) {
|
|
570
|
+
decoratorArgs.push(this.serializeObject(options));
|
|
571
|
+
}
|
|
572
|
+
return `${decorator}(${decoratorArgs.join(', ')})\n`;
|
|
573
|
+
}
|
|
574
|
+
getPropertyIndexes(prop, options) {
|
|
575
|
+
const processIndex = type => {
|
|
576
|
+
const propType = prop[type];
|
|
577
|
+
if (!propType) {
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
const defaultName = this.platform.getIndexName(this.meta.collection, prop.fieldNames, type);
|
|
581
|
+
options[type] = propType === true || defaultName === propType ? 'true' : this.quote(propType);
|
|
582
|
+
const expected = {
|
|
583
|
+
index: this.platform.indexForeignKeys(),
|
|
584
|
+
unique: prop.kind === ReferenceKind.ONE_TO_ONE,
|
|
585
|
+
};
|
|
586
|
+
if (expected[type] && options[type] === 'true') {
|
|
587
|
+
delete options[type];
|
|
588
|
+
}
|
|
589
|
+
};
|
|
590
|
+
processIndex('index');
|
|
591
|
+
processIndex('unique');
|
|
592
|
+
const ret = [];
|
|
593
|
+
let propIndexIsNonTrivialIndex = false;
|
|
594
|
+
const nonTrivialIndexes = this.meta.indexes.filter(
|
|
595
|
+
i => i.properties?.length === 1 && i.properties[0] === prop.name,
|
|
596
|
+
);
|
|
597
|
+
for (const i of nonTrivialIndexes) {
|
|
598
|
+
ret.push(`@${this.referenceDecoratorImport('Index')}(${this.serializeObject(this.getIndexOptions(i, false))})`);
|
|
599
|
+
if (prop.index === i.name) {
|
|
600
|
+
propIndexIsNonTrivialIndex = true;
|
|
601
|
+
delete options.index;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
if (prop.index && !options.index && !propIndexIsNonTrivialIndex) {
|
|
605
|
+
ret.push(
|
|
606
|
+
`@${this.referenceDecoratorImport('Index')}(${typeof prop.index === 'string' ? `{ name: ${this.quote(prop.index)} }` : ''})`,
|
|
607
|
+
);
|
|
608
|
+
}
|
|
609
|
+
let propIndexIsNonTrivialUnique = false;
|
|
610
|
+
const nonTrivialUnique = this.meta.uniques.filter(i => i.properties?.length === 1 && i.properties[0] === prop.name);
|
|
611
|
+
for (const i of nonTrivialUnique) {
|
|
612
|
+
ret.push(`@${this.referenceDecoratorImport('Unique')}(${this.serializeObject(this.getUniqueOptions(i, false))})`);
|
|
613
|
+
if (prop.unique === i.name) {
|
|
614
|
+
propIndexIsNonTrivialUnique = true;
|
|
615
|
+
delete options.unique;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
if (prop.unique && !options.unique && !propIndexIsNonTrivialUnique) {
|
|
619
|
+
ret.push(
|
|
620
|
+
`@${this.referenceDecoratorImport('Unique')}(${typeof prop.unique === 'string' ? `{ name: ${this.quote(prop.unique)} }` : ''})`,
|
|
621
|
+
);
|
|
622
|
+
}
|
|
623
|
+
return ret;
|
|
624
|
+
}
|
|
625
|
+
getCommonDecoratorOptions(options, prop) {
|
|
626
|
+
if (!prop.mappedBy && (prop.nullable || prop.customTypes?.[0]?.prop?.nullable)) {
|
|
627
|
+
options.nullable = true;
|
|
628
|
+
}
|
|
629
|
+
if (prop.primary && (prop.enum || !(typeof prop.kind === 'undefined' || prop.kind === ReferenceKind.SCALAR))) {
|
|
630
|
+
options.primary = true;
|
|
631
|
+
}
|
|
632
|
+
['persist', 'hydrate'].filter(key => prop[key] === false).forEach(key => (options[key] = false));
|
|
633
|
+
['onCreate', 'onUpdate', 'serializer']
|
|
634
|
+
.filter(key => typeof prop[key] === 'function')
|
|
635
|
+
.forEach(key => (options[key] = `${prop[key]}`));
|
|
636
|
+
if (typeof prop.serializedName === 'string') {
|
|
637
|
+
options.serializedName = this.quote(prop.serializedName);
|
|
638
|
+
}
|
|
639
|
+
if (Array.isArray(prop.groups)) {
|
|
640
|
+
options.groups = prop.groups.map(group => this.quote(group));
|
|
641
|
+
}
|
|
642
|
+
['hidden', 'version', 'concurrencyCheck', 'eager', 'lazy', 'orphanRemoval']
|
|
643
|
+
.filter(key => prop[key])
|
|
644
|
+
.forEach(key => (options[key] = true));
|
|
645
|
+
if (prop.cascade && (prop.cascade.length !== 1 || prop.cascade[0] !== Cascade.PERSIST)) {
|
|
646
|
+
options.cascade = `[${prop.cascade.map(value => `${this.referenceCoreImport('Cascade')}.${value.toUpperCase()}`).join(', ')}]`;
|
|
647
|
+
}
|
|
648
|
+
if (typeof prop.comment === 'string') {
|
|
649
|
+
options.comment = this.quote(prop.comment);
|
|
650
|
+
}
|
|
651
|
+
// TODO: Composite FKs with default values require additions to default/defaultRaw that are not yet supported.
|
|
652
|
+
if (prop.fieldNames?.length <= 1) {
|
|
653
|
+
if (
|
|
654
|
+
typeof prop.defaultRaw !== 'undefined' &&
|
|
655
|
+
prop.defaultRaw !== 'null' &&
|
|
656
|
+
prop.defaultRaw !== '' &&
|
|
657
|
+
prop.defaultRaw !== (typeof prop.default === 'string' ? this.quote(prop.default) : `${prop.default}`)
|
|
658
|
+
) {
|
|
659
|
+
options.defaultRaw = `\`${prop.defaultRaw}\``;
|
|
660
|
+
} else if (
|
|
661
|
+
!(typeof prop.default === 'undefined' || prop.default === null) &&
|
|
662
|
+
(prop.ref ||
|
|
663
|
+
(!prop.enum &&
|
|
664
|
+
(typeof prop.kind === 'undefined' || prop.kind === ReferenceKind.SCALAR) &&
|
|
665
|
+
(prop.type === 'unknown' || typeof this.breakdownOfIType(prop) !== 'undefined')))
|
|
666
|
+
) {
|
|
667
|
+
options.default = typeof prop.default === 'string' ? this.quote(prop.default) : prop.default;
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
if (typeof prop.extra === 'string') {
|
|
671
|
+
options.extra = this.quote(prop.extra);
|
|
672
|
+
}
|
|
673
|
+
if (prop.deferMode) {
|
|
674
|
+
options.deferMode = `${this.referenceCoreImport('DeferMode')}.INITIALLY_${prop.deferMode.toUpperCase()}`;
|
|
675
|
+
}
|
|
676
|
+
if (typeof prop.ignoreSchemaChanges !== 'undefined') {
|
|
677
|
+
options.ignoreSchemaChanges ??= [];
|
|
678
|
+
options.ignoreSchemaChanges.push(...prop.ignoreSchemaChanges.map(v => this.quote(v)));
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
#propTypeBreakdowns = new WeakMap();
|
|
682
|
+
breakdownOfIType(prop) {
|
|
683
|
+
if (this.#propTypeBreakdowns.has(prop)) {
|
|
684
|
+
return this.#propTypeBreakdowns.get(prop);
|
|
685
|
+
}
|
|
686
|
+
const mappedDeclaredType = this.platform.getMappedType(prop.type);
|
|
687
|
+
const mappedRawType =
|
|
688
|
+
prop.customTypes?.[0] ??
|
|
689
|
+
(prop.type !== 'unknown' && mappedDeclaredType instanceof UnknownType
|
|
690
|
+
? this.platform.getMappedType(prop.columnTypes[0])
|
|
691
|
+
: mappedDeclaredType);
|
|
692
|
+
const rawType = mappedRawType.runtimeType;
|
|
693
|
+
const mappedSerializedType = prop.customType ?? mappedRawType;
|
|
694
|
+
const serializedType = mappedSerializedType.runtimeType;
|
|
695
|
+
// Add non-lib imports where needed.
|
|
696
|
+
for (const typeSpec of [prop.runtimeType, rawType, serializedType]) {
|
|
697
|
+
const simplePropType = typeSpec.replace(/\[]+$/, '');
|
|
698
|
+
if (!primitivesAndLibs.includes(simplePropType)) {
|
|
699
|
+
this.entityImports.add(simplePropType);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
const nullables = [
|
|
703
|
+
prop.nullable ?? false,
|
|
704
|
+
mappedRawType.prop?.nullable ?? prop.nullable ?? false,
|
|
705
|
+
mappedSerializedType.prop?.nullable ?? prop.nullable ?? false,
|
|
706
|
+
];
|
|
707
|
+
const hasMixedNullability = new Set(nullables).size > 1;
|
|
708
|
+
if (prop.runtimeType !== rawType || rawType !== serializedType || hasMixedNullability) {
|
|
709
|
+
const nullType = this.options.forceUndefined ? ' | undefined' : ' | null';
|
|
710
|
+
if (
|
|
711
|
+
rawType !== serializedType ||
|
|
712
|
+
nullables[1] !== nullables[2] ||
|
|
713
|
+
(prop.hidden && nullables[0] !== nullables[1])
|
|
714
|
+
) {
|
|
715
|
+
const r = [prop.runtimeType, rawType, serializedType];
|
|
716
|
+
if (hasMixedNullability || prop.hidden) {
|
|
717
|
+
for (let i = r.length - 1; i >= 0; --i) {
|
|
718
|
+
if (nullables[i]) {
|
|
719
|
+
r[i] += nullType;
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
if (prop.hidden) {
|
|
723
|
+
r[2] = `(${r[2]}) & ${this.referenceCoreImport('Hidden')}`;
|
|
724
|
+
}
|
|
700
725
|
}
|
|
701
|
-
const r = undefined;
|
|
702
726
|
this.#propTypeBreakdowns.set(prop, r);
|
|
703
727
|
return r;
|
|
728
|
+
}
|
|
729
|
+
const r = [prop.runtimeType, rawType];
|
|
730
|
+
if (hasMixedNullability) {
|
|
731
|
+
for (let i = r.length - 1; i >= 0; --i) {
|
|
732
|
+
if (nullables[i]) {
|
|
733
|
+
r[i] += nullType;
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
this.#propTypeBreakdowns.set(prop, r);
|
|
738
|
+
return r;
|
|
704
739
|
}
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
}
|
|
713
|
-
else if (prop.nativeEnumName) {
|
|
714
|
-
const enumClassName = this.namingStrategy.getEnumClassName(prop.nativeEnumName, undefined, this.meta.schema);
|
|
715
|
-
options.items = `() => ${enumClassName}`;
|
|
716
|
-
options.nativeEnumName = this.quote(prop.nativeEnumName);
|
|
717
|
-
}
|
|
718
|
-
else {
|
|
719
|
-
const enumClassName = this.namingStrategy.getEnumClassName(prop.fieldNames[0], this.meta.collection, this.meta.schema);
|
|
720
|
-
options.items = `() => ${enumClassName}`;
|
|
721
|
-
}
|
|
722
|
-
}
|
|
723
|
-
// For enum properties, we don't need a column type
|
|
724
|
-
// or the property length or other information in the decorator.
|
|
725
|
-
if (prop.enum) {
|
|
726
|
-
return;
|
|
727
|
-
}
|
|
728
|
-
const mappedColumnType = this.platform.getMappedType(prop.columnTypes[0]);
|
|
729
|
-
// If the column's runtimeType matches the declared runtimeType, assume it's the same underlying type.
|
|
730
|
-
const mappedRuntimeType = mappedColumnType.runtimeType === prop.runtimeType
|
|
731
|
-
? mappedColumnType
|
|
732
|
-
: this.platform.getMappedType(prop.runtimeType);
|
|
733
|
-
const mappedDeclaredType = this.platform.getMappedType(prop.type);
|
|
734
|
-
const isTypeStringMissingFromMap = prop.type !== 'unknown' && mappedDeclaredType instanceof UnknownType;
|
|
735
|
-
if (isTypeStringMissingFromMap) {
|
|
736
|
-
this.entityImports.add(prop.type);
|
|
737
|
-
options.type = prop.type;
|
|
738
|
-
}
|
|
739
|
-
else {
|
|
740
|
-
if (this.options.scalarTypeInDecorator || // always output type if forced by the generator options
|
|
741
|
-
(prop.nullable && !this.options.forceUndefined) || // also when there is the "| null" type modifier (because reflect-metadata can't extract the base)
|
|
742
|
-
prop.hidden || // also when there is the "& Hidden" type modifier (because reflect-metadata can't extract the base)
|
|
743
|
-
new Set([
|
|
744
|
-
mappedRuntimeType.name,
|
|
745
|
-
mappedColumnType.name,
|
|
746
|
-
mappedDeclaredType.name,
|
|
747
|
-
this.platform.getMappedType(prop.runtimeType === 'Date' ? 'datetime' : prop.runtimeType).name,
|
|
748
|
-
]).size > 1 || // also, if there's any ambiguity in the type
|
|
749
|
-
(() => {
|
|
750
|
-
const hasUsableNullDefault = prop.nullable && !this.options.forceUndefined && prop.default === null;
|
|
751
|
-
const useDefault = hasUsableNullDefault || !(typeof prop.default === 'undefined' || prop.default === null);
|
|
752
|
-
return (useDefault && !hasUsableNullDefault) || (prop.optional && !prop.nullable);
|
|
753
|
-
})() // also when there is the "| Opt" type modifier (because reflect-metadata can't extract the base)
|
|
754
|
-
) {
|
|
755
|
-
options.type = quote ? this.quote(prop.type) : prop.type;
|
|
756
|
-
}
|
|
757
|
-
}
|
|
758
|
-
const columnTypeFromMappedRuntimeType = mappedRuntimeType.getColumnType({ ...prop, autoincrement: false }, this.platform);
|
|
759
|
-
const columnTypeFromMappedColumnType = mappedColumnType.getColumnType({ ...prop, autoincrement: false }, this.platform);
|
|
760
|
-
const columnTypeFromMappedDeclaredType = mappedDeclaredType.getColumnType({ ...prop, autoincrement: false }, this.platform);
|
|
761
|
-
const needsExplicitColumnType = () => {
|
|
762
|
-
if (isTypeStringMissingFromMap ||
|
|
763
|
-
[mappedRuntimeType, mappedColumnType, columnTypeFromMappedDeclaredType].some(t => t instanceof UnknownType)) {
|
|
764
|
-
return true;
|
|
765
|
-
}
|
|
766
|
-
if (this.platform.normalizeColumnType(prop.columnTypes[0], prop) !==
|
|
767
|
-
this.platform.normalizeColumnType(columnTypeFromMappedColumnType, prop)) {
|
|
768
|
-
return prop.columnTypes[0] !== columnTypeFromMappedColumnType;
|
|
769
|
-
}
|
|
770
|
-
return (columnTypeFromMappedRuntimeType !== columnTypeFromMappedColumnType ||
|
|
771
|
-
columnTypeFromMappedDeclaredType !== columnTypeFromMappedColumnType);
|
|
772
|
-
};
|
|
773
|
-
if (needsExplicitColumnType()) {
|
|
774
|
-
options.columnType = this.quote(prop.columnTypes[0]);
|
|
775
|
-
}
|
|
776
|
-
const assign = (key) => {
|
|
777
|
-
if (prop[key] != null) {
|
|
778
|
-
options[key] = prop[key];
|
|
779
|
-
}
|
|
780
|
-
};
|
|
781
|
-
if (!options.columnType &&
|
|
782
|
-
(typeof mappedColumnType.getDefaultLength === 'undefined' ||
|
|
783
|
-
mappedColumnType.getDefaultLength(this.platform) !== prop.length)) {
|
|
784
|
-
assign('length');
|
|
785
|
-
}
|
|
786
|
-
// those are already included in the `columnType` in most cases, and when that option is present, they would be ignored anyway
|
|
787
|
-
/* v8 ignore next */
|
|
788
|
-
if (mappedColumnType instanceof DecimalType && !options.columnType) {
|
|
789
|
-
assign('precision');
|
|
790
|
-
assign('scale');
|
|
791
|
-
}
|
|
792
|
-
if (this.platform.supportsUnsigned() &&
|
|
793
|
-
((!prop.primary && prop.unsigned) ||
|
|
794
|
-
(prop.primary && !prop.unsigned && this.platform.isNumericColumn(mappedColumnType)))) {
|
|
795
|
-
assign('unsigned');
|
|
796
|
-
}
|
|
797
|
-
if (prop.autoincrement) {
|
|
798
|
-
if (!prop.primary ||
|
|
799
|
-
!this.platform.isNumericColumn(mappedColumnType) ||
|
|
800
|
-
this.meta.getPrimaryProps().length !== 1) {
|
|
801
|
-
options.autoincrement = true;
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
else if (prop.primary &&
|
|
805
|
-
this.platform.isNumericColumn(mappedColumnType) &&
|
|
806
|
-
this.meta.getPrimaryProps().length === 1) {
|
|
807
|
-
options.autoincrement = false;
|
|
808
|
-
}
|
|
809
|
-
if (prop.generated) {
|
|
810
|
-
options.generated = typeof prop.generated === 'string' ? this.quote(prop.generated) : `${prop.generated}`;
|
|
811
|
-
}
|
|
740
|
+
const r = undefined;
|
|
741
|
+
this.#propTypeBreakdowns.set(prop, r);
|
|
742
|
+
return r;
|
|
743
|
+
}
|
|
744
|
+
getScalarPropertyDecoratorOptions(options, prop, quote = true) {
|
|
745
|
+
if (prop.fieldNames[0] !== this.namingStrategy.propertyToColumnName(prop.name)) {
|
|
746
|
+
options.fieldName = this.quote(prop.fieldNames[0]);
|
|
812
747
|
}
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
options.
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
}
|
|
827
|
-
|
|
828
|
-
this.entityImports.add(Utils.className(prop.pivotEntity));
|
|
829
|
-
options.pivotEntity = `() => ${Utils.className(prop.pivotEntity)}`;
|
|
830
|
-
}
|
|
831
|
-
if (prop.joinColumns.length === 1) {
|
|
832
|
-
options.joinColumn = this.quote(prop.joinColumns[0]);
|
|
833
|
-
}
|
|
834
|
-
else {
|
|
835
|
-
options.joinColumns = `[${prop.joinColumns.map(c => this.quote(c)).join(', ')}]`;
|
|
836
|
-
}
|
|
837
|
-
if (prop.inverseJoinColumns.length === 1) {
|
|
838
|
-
options.inverseJoinColumn = this.quote(prop.inverseJoinColumns[0]);
|
|
839
|
-
}
|
|
840
|
-
else {
|
|
841
|
-
options.inverseJoinColumns = `[${prop.inverseJoinColumns.map(c => this.quote(c)).join(', ')}]`;
|
|
842
|
-
}
|
|
843
|
-
if (prop.fixedOrder) {
|
|
844
|
-
options.fixedOrder = true;
|
|
845
|
-
if (prop.fixedOrderColumn && prop.fixedOrderColumn !== this.namingStrategy.referenceColumnName()) {
|
|
846
|
-
options.fixedOrderColumn = this.quote(prop.fixedOrderColumn);
|
|
847
|
-
}
|
|
848
|
-
}
|
|
748
|
+
if (prop.enum) {
|
|
749
|
+
if (this.options.enumMode === 'union-type') {
|
|
750
|
+
options.items = `[${prop.items.map(item => this.quote(item)).join(', ')}]`;
|
|
751
|
+
} else if (prop.nativeEnumName) {
|
|
752
|
+
const enumClassName = this.namingStrategy.getEnumClassName(prop.nativeEnumName, undefined, this.meta.schema);
|
|
753
|
+
options.items = `() => ${enumClassName}`;
|
|
754
|
+
options.nativeEnumName = this.quote(prop.nativeEnumName);
|
|
755
|
+
} else {
|
|
756
|
+
const enumClassName = this.namingStrategy.getEnumClassName(
|
|
757
|
+
prop.fieldNames[0],
|
|
758
|
+
this.meta.collection,
|
|
759
|
+
this.meta.schema,
|
|
760
|
+
);
|
|
761
|
+
options.items = `() => ${enumClassName}`;
|
|
762
|
+
}
|
|
849
763
|
}
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
if (prop.orderBy) {
|
|
855
|
-
options.orderBy = inspect(prop.orderBy);
|
|
856
|
-
}
|
|
764
|
+
// For enum properties, we don't need a column type
|
|
765
|
+
// or the property length or other information in the decorator.
|
|
766
|
+
if (prop.enum) {
|
|
767
|
+
return;
|
|
857
768
|
}
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
769
|
+
const mappedColumnType = this.platform.getMappedType(prop.columnTypes[0]);
|
|
770
|
+
// If the column's runtimeType matches the declared runtimeType, assume it's the same underlying type.
|
|
771
|
+
const mappedRuntimeType =
|
|
772
|
+
mappedColumnType.runtimeType === prop.runtimeType
|
|
773
|
+
? mappedColumnType
|
|
774
|
+
: this.platform.getMappedType(prop.runtimeType);
|
|
775
|
+
const mappedDeclaredType = this.platform.getMappedType(prop.type);
|
|
776
|
+
const isTypeStringMissingFromMap = prop.type !== 'unknown' && mappedDeclaredType instanceof UnknownType;
|
|
777
|
+
if (isTypeStringMissingFromMap) {
|
|
778
|
+
this.entityImports.add(prop.type);
|
|
779
|
+
options.type = prop.type;
|
|
780
|
+
} else {
|
|
781
|
+
if (
|
|
782
|
+
this.options.scalarTypeInDecorator || // always output type if forced by the generator options
|
|
783
|
+
(prop.nullable && !this.options.forceUndefined) || // also when there is the "| null" type modifier (because reflect-metadata can't extract the base)
|
|
784
|
+
prop.hidden || // also when there is the "& Hidden" type modifier (because reflect-metadata can't extract the base)
|
|
785
|
+
new Set([
|
|
786
|
+
mappedRuntimeType.name,
|
|
787
|
+
mappedColumnType.name,
|
|
788
|
+
mappedDeclaredType.name,
|
|
789
|
+
this.platform.getMappedType(prop.runtimeType === 'Date' ? 'datetime' : prop.runtimeType).name,
|
|
790
|
+
]).size > 1 || // also, if there's any ambiguity in the type
|
|
791
|
+
(() => {
|
|
792
|
+
const hasUsableNullDefault = prop.nullable && !this.options.forceUndefined && prop.default === null;
|
|
793
|
+
const useDefault = hasUsableNullDefault || !(typeof prop.default === 'undefined' || prop.default === null);
|
|
794
|
+
return (useDefault && !hasUsableNullDefault) || (prop.optional && !prop.nullable);
|
|
795
|
+
})() // also when there is the "| Opt" type modifier (because reflect-metadata can't extract the base)
|
|
796
|
+
) {
|
|
797
|
+
options.type = quote ? this.quote(prop.type) : prop.type;
|
|
798
|
+
}
|
|
870
799
|
}
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
}
|
|
904
|
-
if (prop.primary) {
|
|
905
|
-
options.primary = true;
|
|
906
|
-
}
|
|
907
|
-
if (prop.generated) {
|
|
908
|
-
options.generated = typeof prop.generated === 'string' ? this.quote(prop.generated) : `${prop.generated}`;
|
|
909
|
-
}
|
|
910
|
-
if (prop.fieldNames.length > 1 && !(typeof prop.default === 'undefined' || prop.default === null)) {
|
|
911
|
-
// TODO: Composite FKs with default values require additions to default/defaultRaw that are not yet supported.
|
|
912
|
-
options.ignoreSchemaChanges = [this.quote('default')];
|
|
913
|
-
}
|
|
800
|
+
const columnTypeFromMappedRuntimeType = mappedRuntimeType.getColumnType(
|
|
801
|
+
{ ...prop, autoincrement: false },
|
|
802
|
+
this.platform,
|
|
803
|
+
);
|
|
804
|
+
const columnTypeFromMappedColumnType = mappedColumnType.getColumnType(
|
|
805
|
+
{ ...prop, autoincrement: false },
|
|
806
|
+
this.platform,
|
|
807
|
+
);
|
|
808
|
+
const columnTypeFromMappedDeclaredType = mappedDeclaredType.getColumnType(
|
|
809
|
+
{ ...prop, autoincrement: false },
|
|
810
|
+
this.platform,
|
|
811
|
+
);
|
|
812
|
+
const needsExplicitColumnType = () => {
|
|
813
|
+
if (
|
|
814
|
+
isTypeStringMissingFromMap ||
|
|
815
|
+
[mappedRuntimeType, mappedColumnType, columnTypeFromMappedDeclaredType].some(t => t instanceof UnknownType)
|
|
816
|
+
) {
|
|
817
|
+
return true;
|
|
818
|
+
}
|
|
819
|
+
if (
|
|
820
|
+
this.platform.normalizeColumnType(prop.columnTypes[0], prop) !==
|
|
821
|
+
this.platform.normalizeColumnType(columnTypeFromMappedColumnType, prop)
|
|
822
|
+
) {
|
|
823
|
+
return prop.columnTypes[0] !== columnTypeFromMappedColumnType;
|
|
824
|
+
}
|
|
825
|
+
return (
|
|
826
|
+
columnTypeFromMappedRuntimeType !== columnTypeFromMappedColumnType ||
|
|
827
|
+
columnTypeFromMappedDeclaredType !== columnTypeFromMappedColumnType
|
|
828
|
+
);
|
|
829
|
+
};
|
|
830
|
+
if (needsExplicitColumnType()) {
|
|
831
|
+
options.columnType = this.quote(prop.columnTypes[0]);
|
|
914
832
|
}
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
833
|
+
const assign = key => {
|
|
834
|
+
if (prop[key] != null) {
|
|
835
|
+
options[key] = prop[key];
|
|
836
|
+
}
|
|
837
|
+
};
|
|
838
|
+
if (
|
|
839
|
+
!options.columnType &&
|
|
840
|
+
(typeof mappedColumnType.getDefaultLength === 'undefined' ||
|
|
841
|
+
mappedColumnType.getDefaultLength(this.platform) !== prop.length)
|
|
842
|
+
) {
|
|
843
|
+
assign('length');
|
|
844
|
+
}
|
|
845
|
+
// those are already included in the `columnType` in most cases, and when that option is present, they would be ignored anyway
|
|
846
|
+
/* v8 ignore next */
|
|
847
|
+
if (mappedColumnType instanceof DecimalType && !options.columnType) {
|
|
848
|
+
assign('precision');
|
|
849
|
+
assign('scale');
|
|
850
|
+
}
|
|
851
|
+
if (
|
|
852
|
+
this.platform.supportsUnsigned() &&
|
|
853
|
+
((!prop.primary && prop.unsigned) ||
|
|
854
|
+
(prop.primary && !prop.unsigned && this.platform.isNumericColumn(mappedColumnType)))
|
|
855
|
+
) {
|
|
856
|
+
assign('unsigned');
|
|
857
|
+
}
|
|
858
|
+
if (prop.autoincrement) {
|
|
859
|
+
if (
|
|
860
|
+
!prop.primary ||
|
|
861
|
+
!this.platform.isNumericColumn(mappedColumnType) ||
|
|
862
|
+
this.meta.getPrimaryProps().length !== 1
|
|
863
|
+
) {
|
|
864
|
+
options.autoincrement = true;
|
|
865
|
+
}
|
|
866
|
+
} else if (
|
|
867
|
+
prop.primary &&
|
|
868
|
+
this.platform.isNumericColumn(mappedColumnType) &&
|
|
869
|
+
this.meta.getPrimaryProps().length === 1
|
|
870
|
+
) {
|
|
871
|
+
options.autoincrement = false;
|
|
872
|
+
}
|
|
873
|
+
if (prop.generated) {
|
|
874
|
+
options.generated = typeof prop.generated === 'string' ? this.quote(prop.generated) : `${prop.generated}`;
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
getManyToManyDecoratorOptions(options, prop) {
|
|
878
|
+
this.entityImports.add(prop.type);
|
|
879
|
+
options.entity = `() => ${prop.type}`;
|
|
880
|
+
if (prop.orderBy) {
|
|
881
|
+
options.orderBy = inspect(prop.orderBy);
|
|
882
|
+
}
|
|
883
|
+
if (prop.mappedBy) {
|
|
884
|
+
options.mappedBy = this.quote(prop.mappedBy);
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
if (
|
|
888
|
+
prop.pivotTable !==
|
|
889
|
+
this.namingStrategy.joinTableName(this.meta.collection, prop.type, prop.name, this.meta.tableName)
|
|
890
|
+
) {
|
|
891
|
+
options.pivotTable = this.quote(prop.pivotTable);
|
|
892
|
+
}
|
|
893
|
+
if (prop.pivotEntity && Utils.className(prop.pivotEntity) !== prop.pivotTable) {
|
|
894
|
+
this.entityImports.add(Utils.className(prop.pivotEntity));
|
|
895
|
+
options.pivotEntity = `() => ${Utils.className(prop.pivotEntity)}`;
|
|
896
|
+
}
|
|
897
|
+
if (prop.joinColumns.length === 1) {
|
|
898
|
+
options.joinColumn = this.quote(prop.joinColumns[0]);
|
|
899
|
+
} else {
|
|
900
|
+
options.joinColumns = `[${prop.joinColumns.map(c => this.quote(c)).join(', ')}]`;
|
|
901
|
+
}
|
|
902
|
+
if (prop.inverseJoinColumns.length === 1) {
|
|
903
|
+
options.inverseJoinColumn = this.quote(prop.inverseJoinColumns[0]);
|
|
904
|
+
} else {
|
|
905
|
+
options.inverseJoinColumns = `[${prop.inverseJoinColumns.map(c => this.quote(c)).join(', ')}]`;
|
|
906
|
+
}
|
|
907
|
+
if (prop.fixedOrder) {
|
|
908
|
+
options.fixedOrder = true;
|
|
909
|
+
if (prop.fixedOrderColumn && prop.fixedOrderColumn !== this.namingStrategy.referenceColumnName()) {
|
|
910
|
+
options.fixedOrderColumn = this.quote(prop.fixedOrderColumn);
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
getOneToManyDecoratorOptions(options, prop) {
|
|
915
|
+
this.entityImports.add(prop.type);
|
|
916
|
+
options.entity = `() => ${prop.type}`;
|
|
917
|
+
options.mappedBy = this.quote(prop.mappedBy);
|
|
918
|
+
if (prop.orderBy) {
|
|
919
|
+
options.orderBy = inspect(prop.orderBy);
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
getEmbeddedPropertyDeclarationOptions(options, prop) {
|
|
923
|
+
this.entityImports.add(prop.type);
|
|
924
|
+
options.entity = `() => ${prop.type}`;
|
|
925
|
+
if (prop.array) {
|
|
926
|
+
options.array = true;
|
|
927
|
+
}
|
|
928
|
+
if (prop.object && !prop.array) {
|
|
929
|
+
options.object = true;
|
|
930
|
+
}
|
|
931
|
+
if (prop.prefix === false || typeof prop.prefix === 'string') {
|
|
932
|
+
options.prefix = prop.prefix === false ? false : this.quote(prop.prefix);
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
getForeignKeyDecoratorOptions(options, prop) {
|
|
936
|
+
this.entityImports.add(prop.type);
|
|
937
|
+
options.entity = `() => ${prop.type}`;
|
|
938
|
+
if (prop.ref) {
|
|
939
|
+
options.ref = true;
|
|
940
|
+
}
|
|
941
|
+
if (prop.mapToPk) {
|
|
942
|
+
options.mapToPk = true;
|
|
943
|
+
}
|
|
944
|
+
if (prop.mappedBy) {
|
|
945
|
+
options.mappedBy = this.quote(prop.mappedBy);
|
|
946
|
+
return;
|
|
947
|
+
}
|
|
948
|
+
if (prop.fieldNames.length === 1) {
|
|
949
|
+
if (prop.fieldNames[0] !== this.namingStrategy.joinKeyColumnName(prop.name, prop.referencedColumnNames[0])) {
|
|
950
|
+
options.fieldName = this.quote(prop.fieldNames[0]);
|
|
951
|
+
}
|
|
952
|
+
} else {
|
|
953
|
+
if (
|
|
954
|
+
prop.fieldNames.length > 1 &&
|
|
955
|
+
prop.fieldNames.some(
|
|
956
|
+
(fieldName, i) =>
|
|
957
|
+
fieldName !== this.namingStrategy.joinKeyColumnName(prop.name, prop.referencedColumnNames[i]),
|
|
958
|
+
)
|
|
959
|
+
) {
|
|
960
|
+
options.fieldNames = prop.fieldNames.map(fieldName => this.quote(fieldName));
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
if (prop.ownColumns && prop.ownColumns.length !== prop.fieldNames.length) {
|
|
964
|
+
options.referencedColumnNames = prop.referencedColumnNames.map(fieldName => this.quote(fieldName));
|
|
965
|
+
}
|
|
966
|
+
if (prop.updateRule) {
|
|
967
|
+
options.updateRule = this.quote(prop.updateRule);
|
|
968
|
+
}
|
|
969
|
+
if (prop.deleteRule) {
|
|
970
|
+
options.deleteRule = this.quote(prop.deleteRule);
|
|
971
|
+
}
|
|
972
|
+
if (prop.primary) {
|
|
973
|
+
options.primary = true;
|
|
974
|
+
}
|
|
975
|
+
if (prop.generated) {
|
|
976
|
+
options.generated = typeof prop.generated === 'string' ? this.quote(prop.generated) : `${prop.generated}`;
|
|
977
|
+
}
|
|
978
|
+
if (prop.fieldNames.length > 1 && !(typeof prop.default === 'undefined' || prop.default === null)) {
|
|
979
|
+
// TODO: Composite FKs with default values require additions to default/defaultRaw that are not yet supported.
|
|
980
|
+
options.ignoreSchemaChanges = [this.quote('default')];
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
getDecoratorType(prop) {
|
|
984
|
+
if (prop.kind === ReferenceKind.ONE_TO_ONE) {
|
|
985
|
+
return 'OneToOne';
|
|
986
|
+
}
|
|
987
|
+
if (prop.kind === ReferenceKind.MANY_TO_ONE) {
|
|
988
|
+
return 'ManyToOne';
|
|
989
|
+
}
|
|
990
|
+
if (prop.kind === ReferenceKind.ONE_TO_MANY) {
|
|
991
|
+
return 'OneToMany';
|
|
992
|
+
}
|
|
993
|
+
if (prop.kind === ReferenceKind.MANY_TO_MANY) {
|
|
994
|
+
return 'ManyToMany';
|
|
995
|
+
}
|
|
996
|
+
if (prop.kind === ReferenceKind.EMBEDDED) {
|
|
997
|
+
return 'Embedded';
|
|
998
|
+
}
|
|
999
|
+
if (prop.enum) {
|
|
1000
|
+
return 'Enum';
|
|
941
1001
|
}
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
return this.options.coreImportsPrefix ? `${this.options.coreImportsPrefix}${identifier}` : identifier;
|
|
1002
|
+
if (prop.primary) {
|
|
1003
|
+
return 'PrimaryKey';
|
|
945
1004
|
}
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
return this.options.coreImportsPrefix ? `${this.options.coreImportsPrefix}${identifier}` : identifier;
|
|
1005
|
+
if (prop.formula) {
|
|
1006
|
+
return 'Formula';
|
|
949
1007
|
}
|
|
1008
|
+
return 'Property';
|
|
1009
|
+
}
|
|
1010
|
+
referenceCoreImport(identifier) {
|
|
1011
|
+
this.coreImports.add(identifier);
|
|
1012
|
+
return this.options.coreImportsPrefix ? `${this.options.coreImportsPrefix}${identifier}` : identifier;
|
|
1013
|
+
}
|
|
1014
|
+
referenceDecoratorImport(identifier) {
|
|
1015
|
+
this.decoratorImports.add(identifier);
|
|
1016
|
+
return this.options.coreImportsPrefix ? `${this.options.coreImportsPrefix}${identifier}` : identifier;
|
|
1017
|
+
}
|
|
950
1018
|
}
|