@mikro-orm/entity-generator 7.0.0-dev.33 → 7.0.0-dev.330

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/SourceFile.js CHANGED
@@ -1,19 +1,20 @@
1
- import { Cascade, Config, DecimalType, ReferenceKind, SCALAR_TYPES, UnknownType, Utils, } from '@mikro-orm/core';
1
+ import { Cascade, Config, DecimalType, ReferenceKind, SCALAR_TYPES, UnknownType, Utils, inspect, } from '@mikro-orm/core';
2
2
  import { parse, relative } from 'node:path';
3
- import { inspect } from 'node:util';
4
3
  import { POSSIBLE_TYPE_IMPORTS } from './CoreImportsHelper.js';
5
4
  /**
6
5
  * @see https://github.com/tc39/proposal-regexp-unicode-property-escapes#other-examples
7
6
  */
8
7
  export const identifierRegex = /^(?:[$_\p{ID_Start}])(?:[$\u200C\u200D\p{ID_Continue}])*$/u;
9
- const primitivesAndLibs = [...SCALAR_TYPES, 'bigint', 'Uint8Array', 'unknown', 'object', 'any'];
8
+ const primitivesAndLibs = [...SCALAR_TYPES, 'unknown', 'object', 'any'];
10
9
  export class SourceFile {
11
10
  meta;
12
11
  namingStrategy;
13
12
  platform;
14
13
  options;
15
14
  coreImports = new Set();
15
+ decoratorImports = new Set();
16
16
  entityImports = new Set();
17
+ enumImports = new Map();
17
18
  constructor(meta, namingStrategy, platform, options) {
18
19
  this.meta = meta;
19
20
  this.namingStrategy = namingStrategy;
@@ -25,24 +26,24 @@ export class SourceFile {
25
26
  if (this.meta.embeddable || this.meta.collection) {
26
27
  if (this.meta.embeddable) {
27
28
  const options = this.getEmbeddableDeclOptions();
28
- ret += `@${this.referenceCoreImport('Embeddable')}(${Utils.hasObjectKeys(options) ? this.serializeObject(options) : ''})\n`;
29
+ ret += `@${this.referenceDecoratorImport('Embeddable')}(${Utils.hasObjectKeys(options) ? this.serializeObject(options) : ''})\n`;
29
30
  }
30
31
  else {
31
32
  const options = this.getEntityDeclOptions();
32
- ret += `@${this.referenceCoreImport('Entity')}(${Utils.hasObjectKeys(options) ? this.serializeObject(options) : ''})\n`;
33
+ ret += `@${this.referenceDecoratorImport('Entity')}(${Utils.hasObjectKeys(options) ? this.serializeObject(options) : ''})\n`;
33
34
  }
34
35
  }
35
36
  for (const index of this.meta.indexes) {
36
37
  if (index.properties?.length === 1 && typeof this.meta.properties[index.properties[0]] !== 'undefined') {
37
38
  continue;
38
39
  }
39
- ret += `@${this.referenceCoreImport('Index')}(${this.serializeObject(this.getIndexOptions(index))})\n`;
40
+ ret += `@${this.referenceDecoratorImport('Index')}(${this.serializeObject(this.getIndexOptions(index))})\n`;
40
41
  }
41
42
  for (const index of this.meta.uniques) {
42
43
  if (index.properties?.length === 1 && typeof this.meta.properties[index.properties[0]] !== 'undefined') {
43
44
  continue;
44
45
  }
45
- ret += `@${this.referenceCoreImport('Unique')}(${this.serializeObject(this.getUniqueOptions(index))})\n`;
46
+ ret += `@${this.referenceDecoratorImport('Unique')}(${this.serializeObject(this.getUniqueOptions(index))})\n`;
46
47
  }
47
48
  let classHead = '';
48
49
  if (this.meta.className === this.options.customBaseEntityName) {
@@ -65,7 +66,10 @@ export class SourceFile {
65
66
  classBody += definition;
66
67
  classBody += '\n';
67
68
  if (prop.enum) {
68
- enumDefinitions.push(this.getEnumClassDefinition(prop, 2));
69
+ const def = this.getEnumClassDefinition(prop, 2);
70
+ if (def.length) {
71
+ enumDefinitions.push(def);
72
+ }
69
73
  }
70
74
  if (prop.eager) {
71
75
  eagerProperties.push(prop);
@@ -94,6 +98,30 @@ export class SourceFile {
94
98
  }
95
99
  return ret;
96
100
  }
101
+ /**
102
+ * Convert index column options to quoted output format.
103
+ */
104
+ getColumnOptions(columns) {
105
+ if (!columns?.length) {
106
+ return undefined;
107
+ }
108
+ return columns.map(col => {
109
+ const colOpt = { name: this.quote(col.name) };
110
+ if (col.sort) {
111
+ colOpt.sort = this.quote(col.sort.toUpperCase());
112
+ }
113
+ if (col.nulls) {
114
+ colOpt.nulls = this.quote(col.nulls.toUpperCase());
115
+ }
116
+ if (col.length != null) {
117
+ colOpt.length = col.length;
118
+ }
119
+ if (col.collation) {
120
+ colOpt.collation = this.quote(col.collation);
121
+ }
122
+ return colOpt;
123
+ });
124
+ }
97
125
  getIndexOptions(index, isAtEntityLevel = true) {
98
126
  const indexOpt = {};
99
127
  if (typeof index.name === 'string') {
@@ -108,6 +136,30 @@ export class SourceFile {
108
136
  if (isAtEntityLevel && index.properties) {
109
137
  indexOpt.properties = Utils.asArray(index.properties).map(prop => this.quote('' + prop));
110
138
  }
139
+ // Index type (e.g., 'fulltext', 'spatial', 'btree', 'hash')
140
+ if (index.type) {
141
+ indexOpt.type = this.quote(index.type);
142
+ }
143
+ // Advanced index options
144
+ const columns = this.getColumnOptions(index.columns);
145
+ if (columns) {
146
+ indexOpt.columns = columns;
147
+ }
148
+ if (index.include) {
149
+ indexOpt.include = Utils.asArray(index.include).map(prop => this.quote('' + prop));
150
+ }
151
+ if (index.fillFactor != null) {
152
+ indexOpt.fillFactor = index.fillFactor;
153
+ }
154
+ if (index.invisible) {
155
+ indexOpt.invisible = true;
156
+ }
157
+ if (index.disabled) {
158
+ indexOpt.disabled = true;
159
+ }
160
+ if (index.clustered) {
161
+ indexOpt.clustered = true;
162
+ }
111
163
  return indexOpt;
112
164
  }
113
165
  getUniqueOptions(index, isAtEntityLevel = true) {
@@ -125,53 +177,97 @@ export class SourceFile {
125
177
  uniqueOpt.properties = Utils.asArray(index.properties).map(prop => this.quote('' + prop));
126
178
  }
127
179
  if (index.deferMode) {
128
- uniqueOpt.deferMode = `${this.referenceCoreImport('DeferMode')}.INITIALLY_${index.deferMode.toUpperCase()}`;
180
+ uniqueOpt.deferMode =
181
+ `${this.referenceCoreImport('DeferMode')}.INITIALLY_${index.deferMode.toUpperCase()}`;
182
+ }
183
+ const columns = this.getColumnOptions(index.columns);
184
+ if (columns) {
185
+ uniqueOpt.columns = columns;
186
+ }
187
+ if (index.include) {
188
+ uniqueOpt.include = Utils.asArray(index.include).map(prop => this.quote('' + prop));
189
+ }
190
+ if (index.fillFactor != null) {
191
+ uniqueOpt.fillFactor = index.fillFactor;
192
+ }
193
+ if (index.disabled) {
194
+ uniqueOpt.disabled = true;
129
195
  }
130
196
  return uniqueOpt;
131
197
  }
132
198
  generateImports() {
133
199
  const imports = new Set();
134
200
  if (this.coreImports.size > 0) {
135
- imports.add(`import { ${([...this.coreImports].sort().map(t => {
201
+ imports.add(`import { ${[...this.coreImports]
202
+ .sort()
203
+ .map(t => {
136
204
  let ret = POSSIBLE_TYPE_IMPORTS.includes(t) ? `type ${t}` : t;
137
205
  if (this.options.coreImportsPrefix) {
138
206
  const resolvedIdentifier = `${this.options.coreImportsPrefix}${t}`;
139
207
  ret += ` as ${resolvedIdentifier}`;
140
208
  }
141
209
  return ret;
142
- }).join(', '))} } from '@mikro-orm/core';`);
210
+ })
211
+ .join(', ')} } from '@mikro-orm/core';`);
212
+ }
213
+ if (this.decoratorImports.size > 0) {
214
+ const type = this.options.decorators;
215
+ imports.add(`import { ${[...this.decoratorImports]
216
+ .sort()
217
+ .map(t => {
218
+ let ret = t;
219
+ if (this.options.coreImportsPrefix) {
220
+ const resolvedIdentifier = `${this.options.coreImportsPrefix}${t}`;
221
+ ret += ` as ${resolvedIdentifier}`;
222
+ }
223
+ return ret;
224
+ })
225
+ .join(', ')} } from '@mikro-orm/decorators/${type}';`);
143
226
  }
144
227
  const extension = this.options.esmImport ? '.js' : '';
145
228
  const { dir, base } = parse(`${this.options.path ?? '.'}/${this.getBaseName()}`);
146
229
  const basePath = relative(dir, this.options.path ?? '.') || '.';
147
230
  (this.options.extraImports?.(basePath, base) ?? []).forEach(v => this.entityImports.add(v));
148
231
  const entityImports = [...this.entityImports].filter(e => e !== this.meta.className);
149
- entityImports.sort().forEach(entity => {
232
+ const importMap = new Map();
233
+ for (const entity of entityImports) {
150
234
  const file = this.options.onImport?.(entity, basePath, extension, base) ?? {
151
235
  path: `${basePath}/${this.options.fileName(entity)}${extension}`,
152
236
  name: entity,
153
237
  };
154
238
  if (file.path === '') {
155
239
  if (file.name === '') {
156
- return;
240
+ continue;
157
241
  }
158
- imports.add(`import ${this.quote(file.name)};`);
159
- return;
242
+ importMap.set(file.path, `import ${this.quote(file.name)};`);
243
+ continue;
160
244
  }
161
245
  if (file.name === '') {
162
- imports.add(`import * as ${entity} from ${this.quote(file.path)};`);
163
- return;
246
+ importMap.set(file.path, `import * as ${entity} from ${this.quote(file.path)};`);
247
+ continue;
164
248
  }
165
249
  if (file.name === 'default') {
166
- imports.add(`import ${entity} from ${this.quote(file.path)};`);
167
- return;
250
+ importMap.set(file.path, `import ${entity} from ${this.quote(file.path)};`);
251
+ continue;
168
252
  }
169
253
  if (file.name === entity) {
170
- imports.add(`import { ${entity} } from ${this.quote(file.path)};`);
171
- return;
254
+ importMap.set(file.path, `import { ${entity} } from ${this.quote(file.path)};`);
255
+ continue;
172
256
  }
173
- imports.add(`import { ${identifierRegex.test(file.name) ? file.name : this.quote(file.name)} as ${entity} } from ${this.quote(file.path)};`);
174
- });
257
+ importMap.set(file.path, `import { ${identifierRegex.test(file.name) ? file.name : this.quote(file.name)} as ${entity} } from ${this.quote(file.path)};`);
258
+ }
259
+ if (this.enumImports.size) {
260
+ for (const [name, exports] of this.enumImports.entries()) {
261
+ const file = this.options.onImport?.(name, basePath, extension, base) ?? {
262
+ path: `${basePath}/${this.options.fileName(name)}${extension}`,
263
+ name,
264
+ };
265
+ importMap.set(file.path, `import { ${exports.join(', ')} } from ${this.quote(file.path)};`);
266
+ }
267
+ }
268
+ for (const key of [...importMap.keys()].sort()) {
269
+ imports.add(importMap.get(key));
270
+ }
175
271
  return Array.from(imports.values()).join('\n');
176
272
  }
177
273
  getEntityClass(classBody) {
@@ -181,8 +277,8 @@ export class SourceFile {
181
277
  }
182
278
  ret += `class ${this.meta.className}`;
183
279
  if (this.meta.extends) {
184
- this.entityImports.add(this.meta.extends);
185
- ret += ` extends ${this.meta.extends}`;
280
+ this.entityImports.add(Utils.className(this.meta.extends));
281
+ ret += ` extends ${Utils.className(this.meta.extends)}`;
186
282
  }
187
283
  else if (this.options.useCoreBaseEntity) {
188
284
  ret += ` extends ${this.referenceCoreImport('BaseEntity')}`;
@@ -201,6 +297,7 @@ export class SourceFile {
201
297
  getPropertyDefinition(prop, padLeft) {
202
298
  const padding = ' '.repeat(padLeft);
203
299
  const propName = identifierRegex.test(prop.name) ? prop.name : this.quote(prop.name);
300
+ const enumMode = this.options.enumMode;
204
301
  let hiddenType = '';
205
302
  if (prop.hidden) {
206
303
  hiddenType += ` & ${this.referenceCoreImport('Hidden')}`;
@@ -218,7 +315,11 @@ export class SourceFile {
218
315
  : (() => {
219
316
  if (isScalar) {
220
317
  if (prop.enum) {
221
- return prop.runtimeType;
318
+ const method = enumMode === 'ts-enum' ? 'getEnumClassName' : 'getEnumTypeName';
319
+ if (prop.nativeEnumName) {
320
+ return this.namingStrategy[method](prop.nativeEnumName, undefined, this.meta.schema);
321
+ }
322
+ return this.namingStrategy[method](prop.fieldNames[0], this.meta.collection, this.meta.schema);
222
323
  }
223
324
  breakdownOfIType = this.breakdownOfIType(prop);
224
325
  if (typeof breakdownOfIType !== 'undefined') {
@@ -231,30 +332,42 @@ export class SourceFile {
231
332
  }
232
333
  return prop.type;
233
334
  })();
234
- const hasUsableNullDefault = (prop.nullable && !this.options.forceUndefined && prop.default === null);
235
- const useDefault = hasUsableNullDefault || (!(typeof prop.default === 'undefined' || prop.default === null) && propType !== 'unknown' && typeof breakdownOfIType === 'undefined');
236
- const optional = (prop.nullable && (this.options.forceUndefined || prop.optional)) ? '?' : (useDefault ? '' : '!');
335
+ const hasUsableNullDefault = prop.nullable && !this.options.forceUndefined && prop.default === null;
336
+ const useDefault = hasUsableNullDefault ||
337
+ (!(typeof prop.default === 'undefined' || prop.default === null) &&
338
+ propType !== 'unknown' &&
339
+ typeof breakdownOfIType === 'undefined');
340
+ const optional = prop.nullable && (this.options.forceUndefined || prop.optional) ? '?' : useDefault ? '' : '!';
237
341
  let ret = `${propName}${optional}: `;
238
342
  const isArray = prop.array && (prop.kind === ReferenceKind.EMBEDDED || prop.enum);
239
343
  const complexType = isArray ? `${propType}[]` : propType;
240
344
  let wrappedType = prop.ref
241
- ? `${this.referenceCoreImport('Ref')}<${complexType}${(isScalar && prop.nullable && !this.options.forceUndefined) ? ' | null' : ''}>`
242
- : ((this.options.esmImport && !isScalar) ? `${this.referenceCoreImport('Rel')}<${complexType}>` : complexType);
243
- if (prop.nullable && !this.options.forceUndefined && (!isScalar || (!prop.ref && !wrappedType.includes(' | null')))) {
345
+ ? `${this.referenceCoreImport('Ref')}<${complexType}${isScalar && prop.nullable && !this.options.forceUndefined ? ' | null' : ''}>`
346
+ : this.options.esmImport && !isScalar
347
+ ? `${this.referenceCoreImport('Rel')}<${complexType}>`
348
+ : complexType;
349
+ if (prop.nullable &&
350
+ !this.options.forceUndefined &&
351
+ (!isScalar || (!prop.ref && !wrappedType.includes(' | null')))) {
244
352
  wrappedType += ' | null';
245
353
  }
246
- const optionalType = (optional !== '?' && prop.optional)
247
- ? ` & ${this.referenceCoreImport('Opt')}`
248
- : '';
249
- ret += (!this.options.forceUndefined && prop.nullable && (hiddenType || optionalType)) ? `(${wrappedType})` : wrappedType;
354
+ const optionalType = optional !== '?' && prop.optional ? ` & ${this.referenceCoreImport('Opt')}` : '';
355
+ ret +=
356
+ !this.options.forceUndefined && prop.nullable && (hiddenType || optionalType) ? `(${wrappedType})` : wrappedType;
250
357
  ret += hiddenType;
251
358
  ret += optionalType;
252
359
  if (!useDefault) {
253
360
  return `${padding}${ret};\n`;
254
361
  }
255
362
  if (prop.enum && typeof prop.default === 'string') {
363
+ if (enumMode === 'union-type') {
364
+ return `${padding}${ret} = ${this.quote(prop.default)};\n`;
365
+ }
366
+ const enumClassName = prop.nativeEnumName
367
+ ? this.namingStrategy.getEnumClassName(prop.nativeEnumName, undefined, this.meta.schema)
368
+ : this.namingStrategy.getEnumClassName(prop.fieldNames[0], this.meta.collection, this.meta.schema);
256
369
  const enumVal = this.namingStrategy.enumValueToEnumProperty(prop.default, prop.fieldNames[0], this.meta.collection, this.meta.schema);
257
- return `${padding}${ret} = ${propType}${identifierRegex.test(enumVal) ? `.${enumVal}` : `[${this.quote(enumVal)}]`};\n`;
370
+ return `${padding}${ret} = ${enumClassName}${identifierRegex.test(enumVal) ? `.${enumVal}` : `[${this.quote(enumVal)}]`};\n`;
258
371
  }
259
372
  if (prop.fieldNames?.length > 1) {
260
373
  // TODO: Composite FKs with default values require additions to default/defaultRaw that are not yet supported.
@@ -270,15 +383,51 @@ export class SourceFile {
270
383
  return `${padding}${ret} = ${prop.ref ? this.referenceCoreImport('ref') : this.referenceCoreImport('rel')}(${propType}, ${defaultVal});\n`;
271
384
  }
272
385
  getEnumClassDefinition(prop, padLeft) {
386
+ const enumMode = this.options.enumMode;
387
+ if (prop.nativeEnumName) {
388
+ const imports = [];
389
+ if (enumMode !== 'union-type') {
390
+ imports.push(prop.runtimeType);
391
+ }
392
+ if (!this.options.inferEntityType && enumMode !== 'ts-enum') {
393
+ const enumTypeName = this.namingStrategy.getEnumTypeName(prop.nativeEnumName, undefined, this.meta.schema);
394
+ imports.push(enumTypeName);
395
+ }
396
+ this.enumImports.set(prop.runtimeType, imports);
397
+ return '';
398
+ }
273
399
  const enumClassName = this.namingStrategy.getEnumClassName(prop.fieldNames[0], this.meta.collection, this.meta.schema);
400
+ const enumTypeName = this.namingStrategy.getEnumTypeName(prop.fieldNames[0], this.meta.collection, this.meta.schema);
274
401
  const padding = ' '.repeat(padLeft);
275
- let ret = `export enum ${enumClassName} {\n`;
276
402
  const enumValues = prop.items;
403
+ if (enumMode === 'union-type') {
404
+ return `export type ${enumTypeName} = ${enumValues.map(item => this.quote(item)).join(' | ')};\n`;
405
+ }
406
+ let ret = '';
407
+ if (enumMode === 'dictionary') {
408
+ ret += `export const ${enumClassName} = {\n`;
409
+ }
410
+ else {
411
+ ret += `export enum ${enumClassName} {\n`;
412
+ }
277
413
  for (const enumValue of enumValues) {
278
414
  const enumName = this.namingStrategy.enumValueToEnumProperty(enumValue, prop.fieldNames[0], this.meta.collection, this.meta.schema);
279
- ret += `${padding}${identifierRegex.test(enumName) ? enumName : this.quote(enumName)} = ${this.quote(enumValue)},\n`;
415
+ if (enumMode === 'dictionary') {
416
+ ret += `${padding}${identifierRegex.test(enumName) ? enumName : this.quote(enumName)}: ${this.quote(enumValue)},\n`;
417
+ }
418
+ else {
419
+ ret += `${padding}${identifierRegex.test(enumName) ? enumName : this.quote(enumName)} = ${this.quote(enumValue)},\n`;
420
+ }
421
+ }
422
+ if (enumMode === 'dictionary') {
423
+ ret += '} as const;\n';
424
+ }
425
+ else {
426
+ ret += '}\n';
427
+ }
428
+ if (enumMode === 'dictionary') {
429
+ ret += `\nexport type ${enumTypeName} = (typeof ${enumClassName})[keyof typeof ${enumClassName}];\n`;
280
430
  }
281
- ret += '}\n';
282
431
  return ret;
283
432
  }
284
433
  serializeObject(options, wordwrap, spaces, level = 0) {
@@ -288,17 +437,19 @@ export class SourceFile {
288
437
  return res;
289
438
  }
290
439
  }
291
- const nextWordwrap = typeof wordwrap === 'number' ? 80 - (spaces ?? 0) - (level * 2) : undefined;
440
+ const nextWordwrap = typeof wordwrap === 'number' ? 80 - (spaces ?? 0) - level * 2 : undefined;
292
441
  const sep = typeof spaces === 'undefined' ? ', ' : `,\n${' '.repeat(spaces)}`;
293
442
  const doIndent = typeof spaces !== 'undefined';
294
443
  if (Array.isArray(options)) {
295
- 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))}` : ''}]`;
444
+ 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)}` : ''}]`;
296
445
  }
297
446
  const entries = Object.entries(options);
298
- return `{${doIndent ? `\n${' '.repeat(spaces)}` : ' '}${entries.map(([opt, val]) => {
447
+ return `{${doIndent ? `\n${' '.repeat(spaces)}` : ' '}${entries
448
+ .map(([opt, val]) => {
299
449
  const key = identifierRegex.test(opt) ? opt : this.quote(opt);
300
- 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)}`;
301
- }).join(sep)}${doIndent ? `${entries.length > 0 ? ',\n' : ''}${' '.repeat(spaces + (level * 2))}` : ' '}}`;
450
+ 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)}`;
451
+ })
452
+ .join(sep)}${doIndent ? `${entries.length > 0 ? ',\n' : ''}${' '.repeat(spaces + level * 2)}` : ' '}}`;
302
453
  }
303
454
  serializeValue(val, wordwrap, spaces, level = 1) {
304
455
  if (typeof val === 'object' && val !== null) {
@@ -308,8 +459,8 @@ export class SourceFile {
308
459
  }
309
460
  getEntityDeclOptions() {
310
461
  const options = {};
311
- if (this.meta.collection !== this.namingStrategy.classToTableName(this.meta.className)) {
312
- options.tableName = this.quote(this.meta.collection);
462
+ if (this.meta.tableName !== this.namingStrategy.classToTableName(this.meta.className)) {
463
+ options.tableName = this.quote(this.meta.tableName);
313
464
  }
314
465
  if (this.meta.schema && this.meta.schema !== this.platform.getDefaultSchemaName()) {
315
466
  options.schema = this.quote(this.meta.schema);
@@ -318,7 +469,7 @@ export class SourceFile {
318
469
  options.expression = this.quote(this.meta.expression);
319
470
  }
320
471
  else if (typeof this.meta.expression === 'function') {
321
- options.expression = `${this.meta.expression}`;
472
+ options.expression = this.meta.expression.toString();
322
473
  }
323
474
  if (this.meta.repositoryClass) {
324
475
  this.entityImports.add(this.meta.repositoryClass);
@@ -337,28 +488,38 @@ export class SourceFile {
337
488
  }
338
489
  getEmbeddableDeclOptions() {
339
490
  const options = {};
340
- return this.getCollectionDecl(options);
491
+ const result = this.getCollectionDecl(options);
492
+ if (result.discriminatorColumn) {
493
+ result.discriminator = result.discriminatorColumn;
494
+ delete result.discriminatorColumn;
495
+ }
496
+ return result;
341
497
  }
342
498
  getCollectionDecl(options) {
343
499
  if (this.meta.abstract) {
344
500
  options.abstract = true;
345
501
  }
346
502
  if (this.meta.discriminatorValue) {
347
- options.discriminatorValue = typeof this.meta.discriminatorValue === 'string' ? this.quote(this.meta.discriminatorValue) : this.meta.discriminatorValue;
503
+ options.discriminatorValue =
504
+ typeof this.meta.discriminatorValue === 'string'
505
+ ? this.quote(this.meta.discriminatorValue)
506
+ : this.meta.discriminatorValue;
348
507
  }
349
508
  if (this.meta.discriminatorColumn) {
350
509
  options.discriminatorColumn = this.quote(this.meta.discriminatorColumn);
351
510
  }
352
511
  if (this.meta.discriminatorMap) {
353
- options.discriminatorMap = Object.fromEntries(Object.entries(this.meta.discriminatorMap)
354
- .map(([discriminatorValue, className]) => [discriminatorValue, this.quote(className)]));
512
+ options.discriminatorMap = Object.fromEntries(Object.entries(this.meta.discriminatorMap).map(([discriminatorValue, cls]) => [
513
+ discriminatorValue,
514
+ this.quote(Utils.className(cls)),
515
+ ]));
355
516
  }
356
517
  return options;
357
518
  }
358
519
  getPropertyDecorator(prop, padLeft) {
359
520
  const padding = ' '.repeat(padLeft);
360
521
  const options = {};
361
- let decorator = `@${this.referenceCoreImport(this.getDecoratorType(prop))}`;
522
+ let decorator = `@${this.referenceDecoratorImport(this.getDecoratorType(prop))}`;
362
523
  if (prop.kind === ReferenceKind.MANY_TO_MANY) {
363
524
  this.getManyToManyDecoratorOptions(options, prop);
364
525
  }
@@ -379,10 +540,10 @@ export class SourceFile {
379
540
  decorator = [...indexes.sort(), decorator].map(d => padding + d).join('\n');
380
541
  const decoratorArgs = [];
381
542
  if (prop.formula) {
382
- decoratorArgs.push(`${prop.formula}`);
543
+ decoratorArgs.push(prop.formula.toString());
383
544
  }
384
545
  if (Utils.hasObjectKeys(options)) {
385
- decoratorArgs.push(`${this.serializeObject(options)}`);
546
+ decoratorArgs.push(this.serializeObject(options));
386
547
  }
387
548
  return `${decorator}(${decoratorArgs.join(', ')})\n`;
388
549
  }
@@ -393,7 +554,7 @@ export class SourceFile {
393
554
  return;
394
555
  }
395
556
  const defaultName = this.platform.getIndexName(this.meta.collection, prop.fieldNames, type);
396
- options[type] = (propType === true || defaultName === propType) ? 'true' : this.quote(propType);
557
+ options[type] = propType === true || defaultName === propType ? 'true' : this.quote(propType);
397
558
  const expected = {
398
559
  index: this.platform.indexForeignKeys(),
399
560
  unique: prop.kind === ReferenceKind.ONE_TO_ONE,
@@ -408,26 +569,26 @@ export class SourceFile {
408
569
  let propIndexIsNonTrivialIndex = false;
409
570
  const nonTrivialIndexes = this.meta.indexes.filter(i => i.properties?.length === 1 && i.properties[0] === prop.name);
410
571
  for (const i of nonTrivialIndexes) {
411
- ret.push(`@${this.referenceCoreImport('Index')}(${this.serializeObject(this.getIndexOptions(i, false))})`);
572
+ ret.push(`@${this.referenceDecoratorImport('Index')}(${this.serializeObject(this.getIndexOptions(i, false))})`);
412
573
  if (prop.index === i.name) {
413
574
  propIndexIsNonTrivialIndex = true;
414
575
  delete options.index;
415
576
  }
416
577
  }
417
578
  if (prop.index && !options.index && !propIndexIsNonTrivialIndex) {
418
- ret.push(`@${this.referenceCoreImport('Index')}(${typeof prop.index === 'string' ? `{ name: ${this.quote(prop.index)} }` : ''})`);
579
+ ret.push(`@${this.referenceDecoratorImport('Index')}(${typeof prop.index === 'string' ? `{ name: ${this.quote(prop.index)} }` : ''})`);
419
580
  }
420
581
  let propIndexIsNonTrivialUnique = false;
421
582
  const nonTrivialUnique = this.meta.uniques.filter(i => i.properties?.length === 1 && i.properties[0] === prop.name);
422
583
  for (const i of nonTrivialUnique) {
423
- ret.push(`@${this.referenceCoreImport('Unique')}(${this.serializeObject(this.getUniqueOptions(i, false))})`);
584
+ ret.push(`@${this.referenceDecoratorImport('Unique')}(${this.serializeObject(this.getUniqueOptions(i, false))})`);
424
585
  if (prop.unique === i.name) {
425
586
  propIndexIsNonTrivialUnique = true;
426
587
  delete options.unique;
427
588
  }
428
589
  }
429
590
  if (prop.unique && !options.unique && !propIndexIsNonTrivialUnique) {
430
- ret.push(`@${this.referenceCoreImport('Unique')}(${typeof prop.unique === 'string' ? `{ name: ${this.quote(prop.unique)} }` : ''})`);
591
+ ret.push(`@${this.referenceDecoratorImport('Unique')}(${typeof prop.unique === 'string' ? `{ name: ${this.quote(prop.unique)} }` : ''})`);
431
592
  }
432
593
  return ret;
433
594
  }
@@ -438,12 +599,10 @@ export class SourceFile {
438
599
  if (prop.primary && (prop.enum || !(typeof prop.kind === 'undefined' || prop.kind === ReferenceKind.SCALAR))) {
439
600
  options.primary = true;
440
601
  }
441
- ['persist', 'hydrate', 'trackChanges']
442
- .filter(key => prop[key] === false)
443
- .forEach(key => options[key] = false);
602
+ ['persist', 'hydrate'].filter(key => prop[key] === false).forEach(key => (options[key] = false));
444
603
  ['onCreate', 'onUpdate', 'serializer']
445
604
  .filter(key => typeof prop[key] === 'function')
446
- .forEach(key => options[key] = `${prop[key]}`);
605
+ .forEach(key => (options[key] = `${prop[key]}`));
447
606
  if (typeof prop.serializedName === 'string') {
448
607
  options.serializedName = this.quote(prop.serializedName);
449
608
  }
@@ -452,7 +611,7 @@ export class SourceFile {
452
611
  }
453
612
  ['hidden', 'version', 'concurrencyCheck', 'eager', 'lazy', 'orphanRemoval']
454
613
  .filter(key => prop[key])
455
- .forEach(key => options[key] = true);
614
+ .forEach(key => (options[key] = true));
456
615
  if (prop.cascade && (prop.cascade.length !== 1 || prop.cascade[0] !== Cascade.PERSIST)) {
457
616
  options.cascade = `[${prop.cascade.map(value => `${this.referenceCoreImport('Cascade')}.${value.toUpperCase()}`).join(', ')}]`;
458
617
  }
@@ -461,11 +620,17 @@ export class SourceFile {
461
620
  }
462
621
  // TODO: Composite FKs with default values require additions to default/defaultRaw that are not yet supported.
463
622
  if (prop.fieldNames?.length <= 1) {
464
- if (typeof prop.defaultRaw !== 'undefined' && prop.defaultRaw !== 'null' && prop.defaultRaw !== '' &&
623
+ if (typeof prop.defaultRaw !== 'undefined' &&
624
+ prop.defaultRaw !== 'null' &&
625
+ prop.defaultRaw !== '' &&
465
626
  prop.defaultRaw !== (typeof prop.default === 'string' ? this.quote(prop.default) : `${prop.default}`)) {
466
627
  options.defaultRaw = `\`${prop.defaultRaw}\``;
467
628
  }
468
- else if (!(typeof prop.default === 'undefined' || prop.default === null) && (prop.ref || (!prop.enum && (typeof prop.kind === 'undefined' || prop.kind === ReferenceKind.SCALAR) && (prop.type === 'unknown' || typeof this.breakdownOfIType(prop) !== 'undefined')))) {
629
+ else if (!(typeof prop.default === 'undefined' || prop.default === null) &&
630
+ (prop.ref ||
631
+ (!prop.enum &&
632
+ (typeof prop.kind === 'undefined' || prop.kind === ReferenceKind.SCALAR) &&
633
+ (prop.type === 'unknown' || typeof this.breakdownOfIType(prop) !== 'undefined')))) {
469
634
  options.default = typeof prop.default === 'string' ? this.quote(prop.default) : prop.default;
470
635
  }
471
636
  }
@@ -480,17 +645,18 @@ export class SourceFile {
480
645
  options.ignoreSchemaChanges.push(...prop.ignoreSchemaChanges.map(v => this.quote(v)));
481
646
  }
482
647
  }
483
- propTypeBreakdowns = new WeakMap();
648
+ #propTypeBreakdowns = new WeakMap();
484
649
  breakdownOfIType(prop) {
485
- if (this.propTypeBreakdowns.has(prop)) {
486
- return this.propTypeBreakdowns.get(prop);
650
+ if (this.#propTypeBreakdowns.has(prop)) {
651
+ return this.#propTypeBreakdowns.get(prop);
487
652
  }
488
653
  const mappedDeclaredType = this.platform.getMappedType(prop.type);
489
- const mappedRawType = (prop.customTypes?.[0] ?? ((prop.type !== 'unknown' && mappedDeclaredType instanceof UnknownType)
490
- ? this.platform.getMappedType(prop.columnTypes[0])
491
- : mappedDeclaredType));
654
+ const mappedRawType = prop.customTypes?.[0] ??
655
+ (prop.type !== 'unknown' && mappedDeclaredType instanceof UnknownType
656
+ ? this.platform.getMappedType(prop.columnTypes[0])
657
+ : mappedDeclaredType);
492
658
  const rawType = mappedRawType.runtimeType;
493
- const mappedSerializedType = (prop.customType ?? mappedRawType);
659
+ const mappedSerializedType = prop.customType ?? mappedRawType;
494
660
  const serializedType = mappedSerializedType.runtimeType;
495
661
  // Add non-lib imports where needed.
496
662
  for (const typeSpec of [prop.runtimeType, rawType, serializedType]) {
@@ -504,10 +670,12 @@ export class SourceFile {
504
670
  mappedRawType.prop?.nullable ?? prop.nullable ?? false,
505
671
  mappedSerializedType.prop?.nullable ?? prop.nullable ?? false,
506
672
  ];
507
- const hasMixedNullability = (new Set(nullables)).size > 1;
673
+ const hasMixedNullability = new Set(nullables).size > 1;
508
674
  if (prop.runtimeType !== rawType || rawType !== serializedType || hasMixedNullability) {
509
675
  const nullType = this.options.forceUndefined ? ' | undefined' : ' | null';
510
- if (rawType !== serializedType || nullables[1] !== nullables[2] || (prop.hidden && nullables[0] !== nullables[1])) {
676
+ if (rawType !== serializedType ||
677
+ nullables[1] !== nullables[2] ||
678
+ (prop.hidden && nullables[0] !== nullables[1])) {
511
679
  const r = [prop.runtimeType, rawType, serializedType];
512
680
  if (hasMixedNullability || prop.hidden) {
513
681
  for (let i = r.length - 1; i >= 0; --i) {
@@ -519,7 +687,7 @@ export class SourceFile {
519
687
  r[2] = `(${r[2]}) & ${this.referenceCoreImport('Hidden')}`;
520
688
  }
521
689
  }
522
- this.propTypeBreakdowns.set(prop, r);
690
+ this.#propTypeBreakdowns.set(prop, r);
523
691
  return r;
524
692
  }
525
693
  const r = [prop.runtimeType, rawType];
@@ -530,19 +698,30 @@ export class SourceFile {
530
698
  }
531
699
  }
532
700
  }
533
- this.propTypeBreakdowns.set(prop, r);
701
+ this.#propTypeBreakdowns.set(prop, r);
534
702
  return r;
535
703
  }
536
704
  const r = undefined;
537
- this.propTypeBreakdowns.set(prop, r);
705
+ this.#propTypeBreakdowns.set(prop, r);
538
706
  return r;
539
707
  }
540
- getScalarPropertyDecoratorOptions(options, prop) {
708
+ getScalarPropertyDecoratorOptions(options, prop, quote = true) {
541
709
  if (prop.fieldNames[0] !== this.namingStrategy.propertyToColumnName(prop.name)) {
542
710
  options.fieldName = this.quote(prop.fieldNames[0]);
543
711
  }
544
712
  if (prop.enum) {
545
- options.items = `() => ${prop.runtimeType}`;
713
+ if (this.options.enumMode === 'union-type') {
714
+ options.items = `[${prop.items.map(item => this.quote(item)).join(', ')}]`;
715
+ }
716
+ else if (prop.nativeEnumName) {
717
+ const enumClassName = this.namingStrategy.getEnumClassName(prop.nativeEnumName, undefined, this.meta.schema);
718
+ options.items = `() => ${enumClassName}`;
719
+ options.nativeEnumName = this.quote(prop.nativeEnumName);
720
+ }
721
+ else {
722
+ const enumClassName = this.namingStrategy.getEnumClassName(prop.fieldNames[0], this.meta.collection, this.meta.schema);
723
+ options.items = `() => ${enumClassName}`;
724
+ }
546
725
  }
547
726
  // For enum properties, we don't need a column type
548
727
  // or the property length or other information in the decorator.
@@ -561,30 +740,38 @@ export class SourceFile {
561
740
  options.type = prop.type;
562
741
  }
563
742
  else {
564
- if (this.options.scalarTypeInDecorator // always output type if forced by the generator options
565
- || (prop.nullable && !this.options.forceUndefined) // also when there is the "| null" type modifier (because reflect-metadata can't extract the base)
566
- || prop.hidden // also when there is the "& Hidden" type modifier (because reflect-metadata can't extract the base)
567
- || (new Set([mappedRuntimeType.name, mappedColumnType.name, mappedDeclaredType.name, this.platform.getMappedType(prop.runtimeType === 'Date' ? 'datetime' : prop.runtimeType).name])).size > 1 // also, if there's any ambiguity in the type
568
- || (() => {
569
- const hasUsableNullDefault = (prop.nullable && !this.options.forceUndefined && prop.default === null);
743
+ if (this.options.scalarTypeInDecorator || // always output type if forced by the generator options
744
+ (prop.nullable && !this.options.forceUndefined) || // also when there is the "| null" type modifier (because reflect-metadata can't extract the base)
745
+ prop.hidden || // also when there is the "& Hidden" type modifier (because reflect-metadata can't extract the base)
746
+ new Set([
747
+ mappedRuntimeType.name,
748
+ mappedColumnType.name,
749
+ mappedDeclaredType.name,
750
+ this.platform.getMappedType(prop.runtimeType === 'Date' ? 'datetime' : prop.runtimeType).name,
751
+ ]).size > 1 || // also, if there's any ambiguity in the type
752
+ (() => {
753
+ const hasUsableNullDefault = prop.nullable && !this.options.forceUndefined && prop.default === null;
570
754
  const useDefault = hasUsableNullDefault || !(typeof prop.default === 'undefined' || prop.default === null);
571
- return ((useDefault && !hasUsableNullDefault) || (prop.optional && !prop.nullable));
755
+ return (useDefault && !hasUsableNullDefault) || (prop.optional && !prop.nullable);
572
756
  })() // also when there is the "| Opt" type modifier (because reflect-metadata can't extract the base)
573
757
  ) {
574
- options.type = this.quote(prop.type);
758
+ options.type = quote ? this.quote(prop.type) : prop.type;
575
759
  }
576
760
  }
577
761
  const columnTypeFromMappedRuntimeType = mappedRuntimeType.getColumnType({ ...prop, autoincrement: false }, this.platform);
578
762
  const columnTypeFromMappedColumnType = mappedColumnType.getColumnType({ ...prop, autoincrement: false }, this.platform);
579
763
  const columnTypeFromMappedDeclaredType = mappedDeclaredType.getColumnType({ ...prop, autoincrement: false }, this.platform);
580
764
  const needsExplicitColumnType = () => {
581
- if (isTypeStringMissingFromMap || [mappedRuntimeType, mappedColumnType, columnTypeFromMappedDeclaredType].some(t => t instanceof UnknownType)) {
765
+ if (isTypeStringMissingFromMap ||
766
+ [mappedRuntimeType, mappedColumnType, columnTypeFromMappedDeclaredType].some(t => t instanceof UnknownType)) {
582
767
  return true;
583
768
  }
584
- if (this.platform.normalizeColumnType(prop.columnTypes[0], prop) !== this.platform.normalizeColumnType(columnTypeFromMappedColumnType, prop)) {
769
+ if (this.platform.normalizeColumnType(prop.columnTypes[0], prop) !==
770
+ this.platform.normalizeColumnType(columnTypeFromMappedColumnType, prop)) {
585
771
  return prop.columnTypes[0] !== columnTypeFromMappedColumnType;
586
772
  }
587
- return columnTypeFromMappedRuntimeType !== columnTypeFromMappedColumnType || columnTypeFromMappedDeclaredType !== columnTypeFromMappedColumnType;
773
+ return (columnTypeFromMappedRuntimeType !== columnTypeFromMappedColumnType ||
774
+ columnTypeFromMappedDeclaredType !== columnTypeFromMappedColumnType);
588
775
  };
589
776
  if (needsExplicitColumnType()) {
590
777
  options.columnType = this.quote(prop.columnTypes[0]);
@@ -594,11 +781,13 @@ export class SourceFile {
594
781
  options[key] = prop[key];
595
782
  }
596
783
  };
597
- if (!options.columnType && (typeof mappedColumnType.getDefaultLength === 'undefined' || mappedColumnType.getDefaultLength(this.platform) !== prop.length)) {
784
+ if (!options.columnType &&
785
+ (typeof mappedColumnType.getDefaultLength === 'undefined' ||
786
+ mappedColumnType.getDefaultLength(this.platform) !== prop.length)) {
598
787
  assign('length');
599
788
  }
600
789
  // those are already included in the `columnType` in most cases, and when that option is present, they would be ignored anyway
601
- /* v8 ignore next 4 */
790
+ /* v8 ignore next */
602
791
  if (mappedColumnType instanceof DecimalType && !options.columnType) {
603
792
  assign('precision');
604
793
  assign('scale');
@@ -609,14 +798,16 @@ export class SourceFile {
609
798
  assign('unsigned');
610
799
  }
611
800
  if (prop.autoincrement) {
612
- if (!prop.primary || !this.platform.isNumericColumn(mappedColumnType) || this.meta.getPrimaryProps().length !== 1) {
801
+ if (!prop.primary ||
802
+ !this.platform.isNumericColumn(mappedColumnType) ||
803
+ this.meta.getPrimaryProps().length !== 1) {
613
804
  options.autoincrement = true;
614
805
  }
615
806
  }
616
- else {
617
- if (prop.primary && this.platform.isNumericColumn(mappedColumnType) && this.meta.getPrimaryProps().length === 1) {
618
- options.autoincrement = false;
619
- }
807
+ else if (prop.primary &&
808
+ this.platform.isNumericColumn(mappedColumnType) &&
809
+ this.meta.getPrimaryProps().length === 1) {
810
+ options.autoincrement = false;
620
811
  }
621
812
  if (prop.generated) {
622
813
  options.generated = typeof prop.generated === 'string' ? this.quote(prop.generated) : `${prop.generated}`;
@@ -632,24 +823,25 @@ export class SourceFile {
632
823
  options.mappedBy = this.quote(prop.mappedBy);
633
824
  return;
634
825
  }
635
- if (prop.pivotTable !== this.namingStrategy.joinTableName(this.meta.collection, prop.type, prop.name)) {
826
+ if (prop.pivotTable !==
827
+ this.namingStrategy.joinTableName(this.meta.collection, prop.type, prop.name, this.meta.tableName)) {
636
828
  options.pivotTable = this.quote(prop.pivotTable);
637
829
  }
638
- if (prop.pivotEntity && prop.pivotEntity !== prop.pivotTable) {
639
- this.entityImports.add(prop.pivotEntity);
640
- options.pivotEntity = `() => ${prop.pivotEntity}`;
830
+ if (prop.pivotEntity && Utils.className(prop.pivotEntity) !== prop.pivotTable) {
831
+ this.entityImports.add(Utils.className(prop.pivotEntity));
832
+ options.pivotEntity = `() => ${Utils.className(prop.pivotEntity)}`;
641
833
  }
642
834
  if (prop.joinColumns.length === 1) {
643
835
  options.joinColumn = this.quote(prop.joinColumns[0]);
644
836
  }
645
837
  else {
646
- options.joinColumns = `[${prop.joinColumns.map(this.quote).join(', ')}]`;
838
+ options.joinColumns = `[${prop.joinColumns.map(c => this.quote(c)).join(', ')}]`;
647
839
  }
648
840
  if (prop.inverseJoinColumns.length === 1) {
649
841
  options.inverseJoinColumn = this.quote(prop.inverseJoinColumns[0]);
650
842
  }
651
843
  else {
652
- options.inverseJoinColumns = `[${prop.inverseJoinColumns.map(this.quote).join(', ')}]`;
844
+ options.inverseJoinColumns = `[${prop.inverseJoinColumns.map(c => this.quote(c)).join(', ')}]`;
653
845
  }
654
846
  if (prop.fixedOrder) {
655
847
  options.fixedOrder = true;
@@ -672,7 +864,7 @@ export class SourceFile {
672
864
  if (prop.array) {
673
865
  options.array = true;
674
866
  }
675
- if (prop.object) {
867
+ if (prop.object && !prop.array) {
676
868
  options.object = true;
677
869
  }
678
870
  if (prop.prefix === false || typeof prop.prefix === 'string') {
@@ -698,17 +890,18 @@ export class SourceFile {
698
890
  }
699
891
  }
700
892
  else {
701
- if (prop.fieldNames.length > 1 && prop.fieldNames.some((fieldName, i) => fieldName !== this.namingStrategy.joinKeyColumnName(prop.name, prop.referencedColumnNames[i]))) {
893
+ if (prop.fieldNames.length > 1 &&
894
+ prop.fieldNames.some((fieldName, i) => fieldName !== this.namingStrategy.joinKeyColumnName(prop.name, prop.referencedColumnNames[i]))) {
702
895
  options.fieldNames = prop.fieldNames.map(fieldName => this.quote(fieldName));
703
896
  }
704
897
  }
705
898
  if (prop.ownColumns && prop.ownColumns.length !== prop.fieldNames.length) {
706
899
  options.referencedColumnNames = prop.referencedColumnNames.map(fieldName => this.quote(fieldName));
707
900
  }
708
- if (!['no action', 'restrict'].includes(prop.updateRule.toLowerCase())) {
901
+ if (prop.updateRule) {
709
902
  options.updateRule = this.quote(prop.updateRule);
710
903
  }
711
- if (!['no action', 'restrict'].includes(prop.deleteRule.toLowerCase())) {
904
+ if (prop.deleteRule) {
712
905
  options.deleteRule = this.quote(prop.deleteRule);
713
906
  }
714
907
  if (prop.primary) {
@@ -751,8 +944,10 @@ export class SourceFile {
751
944
  }
752
945
  referenceCoreImport(identifier) {
753
946
  this.coreImports.add(identifier);
754
- return this.options.coreImportsPrefix
755
- ? `${this.options.coreImportsPrefix}${identifier}`
756
- : identifier;
947
+ return this.options.coreImportsPrefix ? `${this.options.coreImportsPrefix}${identifier}` : identifier;
948
+ }
949
+ referenceDecoratorImport(identifier) {
950
+ this.decoratorImports.add(identifier);
951
+ return this.options.coreImportsPrefix ? `${this.options.coreImportsPrefix}${identifier}` : identifier;
757
952
  }
758
953
  }