@mikro-orm/entity-generator 7.0.0-dev.31 → 7.0.0-dev.311

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);
@@ -344,21 +495,26 @@ export class SourceFile {
344
495
  options.abstract = true;
345
496
  }
346
497
  if (this.meta.discriminatorValue) {
347
- options.discriminatorValue = typeof this.meta.discriminatorValue === 'string' ? this.quote(this.meta.discriminatorValue) : this.meta.discriminatorValue;
498
+ options.discriminatorValue =
499
+ typeof this.meta.discriminatorValue === 'string'
500
+ ? this.quote(this.meta.discriminatorValue)
501
+ : this.meta.discriminatorValue;
348
502
  }
349
503
  if (this.meta.discriminatorColumn) {
350
504
  options.discriminatorColumn = this.quote(this.meta.discriminatorColumn);
351
505
  }
352
506
  if (this.meta.discriminatorMap) {
353
- options.discriminatorMap = Object.fromEntries(Object.entries(this.meta.discriminatorMap)
354
- .map(([discriminatorValue, className]) => [discriminatorValue, this.quote(className)]));
507
+ options.discriminatorMap = Object.fromEntries(Object.entries(this.meta.discriminatorMap).map(([discriminatorValue, cls]) => [
508
+ discriminatorValue,
509
+ this.quote(Utils.className(cls)),
510
+ ]));
355
511
  }
356
512
  return options;
357
513
  }
358
514
  getPropertyDecorator(prop, padLeft) {
359
515
  const padding = ' '.repeat(padLeft);
360
516
  const options = {};
361
- let decorator = `@${this.referenceCoreImport(this.getDecoratorType(prop))}`;
517
+ let decorator = `@${this.referenceDecoratorImport(this.getDecoratorType(prop))}`;
362
518
  if (prop.kind === ReferenceKind.MANY_TO_MANY) {
363
519
  this.getManyToManyDecoratorOptions(options, prop);
364
520
  }
@@ -379,10 +535,10 @@ export class SourceFile {
379
535
  decorator = [...indexes.sort(), decorator].map(d => padding + d).join('\n');
380
536
  const decoratorArgs = [];
381
537
  if (prop.formula) {
382
- decoratorArgs.push(`${prop.formula}`);
538
+ decoratorArgs.push(prop.formula.toString());
383
539
  }
384
540
  if (Utils.hasObjectKeys(options)) {
385
- decoratorArgs.push(`${this.serializeObject(options)}`);
541
+ decoratorArgs.push(this.serializeObject(options));
386
542
  }
387
543
  return `${decorator}(${decoratorArgs.join(', ')})\n`;
388
544
  }
@@ -393,7 +549,7 @@ export class SourceFile {
393
549
  return;
394
550
  }
395
551
  const defaultName = this.platform.getIndexName(this.meta.collection, prop.fieldNames, type);
396
- options[type] = (propType === true || defaultName === propType) ? 'true' : this.quote(propType);
552
+ options[type] = propType === true || defaultName === propType ? 'true' : this.quote(propType);
397
553
  const expected = {
398
554
  index: this.platform.indexForeignKeys(),
399
555
  unique: prop.kind === ReferenceKind.ONE_TO_ONE,
@@ -408,26 +564,26 @@ export class SourceFile {
408
564
  let propIndexIsNonTrivialIndex = false;
409
565
  const nonTrivialIndexes = this.meta.indexes.filter(i => i.properties?.length === 1 && i.properties[0] === prop.name);
410
566
  for (const i of nonTrivialIndexes) {
411
- ret.push(`@${this.referenceCoreImport('Index')}(${this.serializeObject(this.getIndexOptions(i, false))})`);
567
+ ret.push(`@${this.referenceDecoratorImport('Index')}(${this.serializeObject(this.getIndexOptions(i, false))})`);
412
568
  if (prop.index === i.name) {
413
569
  propIndexIsNonTrivialIndex = true;
414
570
  delete options.index;
415
571
  }
416
572
  }
417
573
  if (prop.index && !options.index && !propIndexIsNonTrivialIndex) {
418
- ret.push(`@${this.referenceCoreImport('Index')}(${typeof prop.index === 'string' ? `{ name: ${this.quote(prop.index)} }` : ''})`);
574
+ ret.push(`@${this.referenceDecoratorImport('Index')}(${typeof prop.index === 'string' ? `{ name: ${this.quote(prop.index)} }` : ''})`);
419
575
  }
420
576
  let propIndexIsNonTrivialUnique = false;
421
577
  const nonTrivialUnique = this.meta.uniques.filter(i => i.properties?.length === 1 && i.properties[0] === prop.name);
422
578
  for (const i of nonTrivialUnique) {
423
- ret.push(`@${this.referenceCoreImport('Unique')}(${this.serializeObject(this.getUniqueOptions(i, false))})`);
579
+ ret.push(`@${this.referenceDecoratorImport('Unique')}(${this.serializeObject(this.getUniqueOptions(i, false))})`);
424
580
  if (prop.unique === i.name) {
425
581
  propIndexIsNonTrivialUnique = true;
426
582
  delete options.unique;
427
583
  }
428
584
  }
429
585
  if (prop.unique && !options.unique && !propIndexIsNonTrivialUnique) {
430
- ret.push(`@${this.referenceCoreImport('Unique')}(${typeof prop.unique === 'string' ? `{ name: ${this.quote(prop.unique)} }` : ''})`);
586
+ ret.push(`@${this.referenceDecoratorImport('Unique')}(${typeof prop.unique === 'string' ? `{ name: ${this.quote(prop.unique)} }` : ''})`);
431
587
  }
432
588
  return ret;
433
589
  }
@@ -438,12 +594,10 @@ export class SourceFile {
438
594
  if (prop.primary && (prop.enum || !(typeof prop.kind === 'undefined' || prop.kind === ReferenceKind.SCALAR))) {
439
595
  options.primary = true;
440
596
  }
441
- ['persist', 'hydrate', 'trackChanges']
442
- .filter(key => prop[key] === false)
443
- .forEach(key => options[key] = false);
597
+ ['persist', 'hydrate'].filter(key => prop[key] === false).forEach(key => (options[key] = false));
444
598
  ['onCreate', 'onUpdate', 'serializer']
445
599
  .filter(key => typeof prop[key] === 'function')
446
- .forEach(key => options[key] = `${prop[key]}`);
600
+ .forEach(key => (options[key] = `${prop[key]}`));
447
601
  if (typeof prop.serializedName === 'string') {
448
602
  options.serializedName = this.quote(prop.serializedName);
449
603
  }
@@ -452,7 +606,7 @@ export class SourceFile {
452
606
  }
453
607
  ['hidden', 'version', 'concurrencyCheck', 'eager', 'lazy', 'orphanRemoval']
454
608
  .filter(key => prop[key])
455
- .forEach(key => options[key] = true);
609
+ .forEach(key => (options[key] = true));
456
610
  if (prop.cascade && (prop.cascade.length !== 1 || prop.cascade[0] !== Cascade.PERSIST)) {
457
611
  options.cascade = `[${prop.cascade.map(value => `${this.referenceCoreImport('Cascade')}.${value.toUpperCase()}`).join(', ')}]`;
458
612
  }
@@ -461,11 +615,17 @@ export class SourceFile {
461
615
  }
462
616
  // TODO: Composite FKs with default values require additions to default/defaultRaw that are not yet supported.
463
617
  if (prop.fieldNames?.length <= 1) {
464
- if (typeof prop.defaultRaw !== 'undefined' && prop.defaultRaw !== 'null' && prop.defaultRaw !== '' &&
618
+ if (typeof prop.defaultRaw !== 'undefined' &&
619
+ prop.defaultRaw !== 'null' &&
620
+ prop.defaultRaw !== '' &&
465
621
  prop.defaultRaw !== (typeof prop.default === 'string' ? this.quote(prop.default) : `${prop.default}`)) {
466
622
  options.defaultRaw = `\`${prop.defaultRaw}\``;
467
623
  }
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')))) {
624
+ else if (!(typeof prop.default === 'undefined' || prop.default === null) &&
625
+ (prop.ref ||
626
+ (!prop.enum &&
627
+ (typeof prop.kind === 'undefined' || prop.kind === ReferenceKind.SCALAR) &&
628
+ (prop.type === 'unknown' || typeof this.breakdownOfIType(prop) !== 'undefined')))) {
469
629
  options.default = typeof prop.default === 'string' ? this.quote(prop.default) : prop.default;
470
630
  }
471
631
  }
@@ -486,11 +646,12 @@ export class SourceFile {
486
646
  return this.propTypeBreakdowns.get(prop);
487
647
  }
488
648
  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));
649
+ const mappedRawType = prop.customTypes?.[0] ??
650
+ (prop.type !== 'unknown' && mappedDeclaredType instanceof UnknownType
651
+ ? this.platform.getMappedType(prop.columnTypes[0])
652
+ : mappedDeclaredType);
492
653
  const rawType = mappedRawType.runtimeType;
493
- const mappedSerializedType = (prop.customType ?? mappedRawType);
654
+ const mappedSerializedType = prop.customType ?? mappedRawType;
494
655
  const serializedType = mappedSerializedType.runtimeType;
495
656
  // Add non-lib imports where needed.
496
657
  for (const typeSpec of [prop.runtimeType, rawType, serializedType]) {
@@ -504,10 +665,12 @@ export class SourceFile {
504
665
  mappedRawType.prop?.nullable ?? prop.nullable ?? false,
505
666
  mappedSerializedType.prop?.nullable ?? prop.nullable ?? false,
506
667
  ];
507
- const hasMixedNullability = (new Set(nullables)).size > 1;
668
+ const hasMixedNullability = new Set(nullables).size > 1;
508
669
  if (prop.runtimeType !== rawType || rawType !== serializedType || hasMixedNullability) {
509
670
  const nullType = this.options.forceUndefined ? ' | undefined' : ' | null';
510
- if (rawType !== serializedType || nullables[1] !== nullables[2] || (prop.hidden && nullables[0] !== nullables[1])) {
671
+ if (rawType !== serializedType ||
672
+ nullables[1] !== nullables[2] ||
673
+ (prop.hidden && nullables[0] !== nullables[1])) {
511
674
  const r = [prop.runtimeType, rawType, serializedType];
512
675
  if (hasMixedNullability || prop.hidden) {
513
676
  for (let i = r.length - 1; i >= 0; --i) {
@@ -537,12 +700,23 @@ export class SourceFile {
537
700
  this.propTypeBreakdowns.set(prop, r);
538
701
  return r;
539
702
  }
540
- getScalarPropertyDecoratorOptions(options, prop) {
703
+ getScalarPropertyDecoratorOptions(options, prop, quote = true) {
541
704
  if (prop.fieldNames[0] !== this.namingStrategy.propertyToColumnName(prop.name)) {
542
705
  options.fieldName = this.quote(prop.fieldNames[0]);
543
706
  }
544
707
  if (prop.enum) {
545
- options.items = `() => ${prop.runtimeType}`;
708
+ if (this.options.enumMode === 'union-type') {
709
+ options.items = `[${prop.items.map(item => this.quote(item)).join(', ')}]`;
710
+ }
711
+ else if (prop.nativeEnumName) {
712
+ const enumClassName = this.namingStrategy.getEnumClassName(prop.nativeEnumName, undefined, this.meta.schema);
713
+ options.items = `() => ${enumClassName}`;
714
+ options.nativeEnumName = this.quote(prop.nativeEnumName);
715
+ }
716
+ else {
717
+ const enumClassName = this.namingStrategy.getEnumClassName(prop.fieldNames[0], this.meta.collection, this.meta.schema);
718
+ options.items = `() => ${enumClassName}`;
719
+ }
546
720
  }
547
721
  // For enum properties, we don't need a column type
548
722
  // or the property length or other information in the decorator.
@@ -561,30 +735,38 @@ export class SourceFile {
561
735
  options.type = prop.type;
562
736
  }
563
737
  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);
738
+ if (this.options.scalarTypeInDecorator || // always output type if forced by the generator options
739
+ (prop.nullable && !this.options.forceUndefined) || // also when there is the "| null" type modifier (because reflect-metadata can't extract the base)
740
+ prop.hidden || // also when there is the "& Hidden" type modifier (because reflect-metadata can't extract the base)
741
+ new Set([
742
+ mappedRuntimeType.name,
743
+ mappedColumnType.name,
744
+ mappedDeclaredType.name,
745
+ this.platform.getMappedType(prop.runtimeType === 'Date' ? 'datetime' : prop.runtimeType).name,
746
+ ]).size > 1 || // also, if there's any ambiguity in the type
747
+ (() => {
748
+ const hasUsableNullDefault = prop.nullable && !this.options.forceUndefined && prop.default === null;
570
749
  const useDefault = hasUsableNullDefault || !(typeof prop.default === 'undefined' || prop.default === null);
571
- return ((useDefault && !hasUsableNullDefault) || (prop.optional && !prop.nullable));
750
+ return (useDefault && !hasUsableNullDefault) || (prop.optional && !prop.nullable);
572
751
  })() // also when there is the "| Opt" type modifier (because reflect-metadata can't extract the base)
573
752
  ) {
574
- options.type = this.quote(prop.type);
753
+ options.type = quote ? this.quote(prop.type) : prop.type;
575
754
  }
576
755
  }
577
756
  const columnTypeFromMappedRuntimeType = mappedRuntimeType.getColumnType({ ...prop, autoincrement: false }, this.platform);
578
757
  const columnTypeFromMappedColumnType = mappedColumnType.getColumnType({ ...prop, autoincrement: false }, this.platform);
579
758
  const columnTypeFromMappedDeclaredType = mappedDeclaredType.getColumnType({ ...prop, autoincrement: false }, this.platform);
580
759
  const needsExplicitColumnType = () => {
581
- if (isTypeStringMissingFromMap || [mappedRuntimeType, mappedColumnType, columnTypeFromMappedDeclaredType].some(t => t instanceof UnknownType)) {
760
+ if (isTypeStringMissingFromMap ||
761
+ [mappedRuntimeType, mappedColumnType, columnTypeFromMappedDeclaredType].some(t => t instanceof UnknownType)) {
582
762
  return true;
583
763
  }
584
- if (this.platform.normalizeColumnType(prop.columnTypes[0], prop) !== this.platform.normalizeColumnType(columnTypeFromMappedColumnType, prop)) {
764
+ if (this.platform.normalizeColumnType(prop.columnTypes[0], prop) !==
765
+ this.platform.normalizeColumnType(columnTypeFromMappedColumnType, prop)) {
585
766
  return prop.columnTypes[0] !== columnTypeFromMappedColumnType;
586
767
  }
587
- return columnTypeFromMappedRuntimeType !== columnTypeFromMappedColumnType || columnTypeFromMappedDeclaredType !== columnTypeFromMappedColumnType;
768
+ return (columnTypeFromMappedRuntimeType !== columnTypeFromMappedColumnType ||
769
+ columnTypeFromMappedDeclaredType !== columnTypeFromMappedColumnType);
588
770
  };
589
771
  if (needsExplicitColumnType()) {
590
772
  options.columnType = this.quote(prop.columnTypes[0]);
@@ -594,11 +776,13 @@ export class SourceFile {
594
776
  options[key] = prop[key];
595
777
  }
596
778
  };
597
- if (!options.columnType && (typeof mappedColumnType.getDefaultLength === 'undefined' || mappedColumnType.getDefaultLength(this.platform) !== prop.length)) {
779
+ if (!options.columnType &&
780
+ (typeof mappedColumnType.getDefaultLength === 'undefined' ||
781
+ mappedColumnType.getDefaultLength(this.platform) !== prop.length)) {
598
782
  assign('length');
599
783
  }
600
784
  // 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 */
785
+ /* v8 ignore next */
602
786
  if (mappedColumnType instanceof DecimalType && !options.columnType) {
603
787
  assign('precision');
604
788
  assign('scale');
@@ -609,14 +793,16 @@ export class SourceFile {
609
793
  assign('unsigned');
610
794
  }
611
795
  if (prop.autoincrement) {
612
- if (!prop.primary || !this.platform.isNumericColumn(mappedColumnType) || this.meta.getPrimaryProps().length !== 1) {
796
+ if (!prop.primary ||
797
+ !this.platform.isNumericColumn(mappedColumnType) ||
798
+ this.meta.getPrimaryProps().length !== 1) {
613
799
  options.autoincrement = true;
614
800
  }
615
801
  }
616
- else {
617
- if (prop.primary && this.platform.isNumericColumn(mappedColumnType) && this.meta.getPrimaryProps().length === 1) {
618
- options.autoincrement = false;
619
- }
802
+ else if (prop.primary &&
803
+ this.platform.isNumericColumn(mappedColumnType) &&
804
+ this.meta.getPrimaryProps().length === 1) {
805
+ options.autoincrement = false;
620
806
  }
621
807
  if (prop.generated) {
622
808
  options.generated = typeof prop.generated === 'string' ? this.quote(prop.generated) : `${prop.generated}`;
@@ -632,24 +818,25 @@ export class SourceFile {
632
818
  options.mappedBy = this.quote(prop.mappedBy);
633
819
  return;
634
820
  }
635
- if (prop.pivotTable !== this.namingStrategy.joinTableName(this.meta.collection, prop.type, prop.name)) {
821
+ if (prop.pivotTable !==
822
+ this.namingStrategy.joinTableName(this.meta.collection, prop.type, prop.name, this.meta.tableName)) {
636
823
  options.pivotTable = this.quote(prop.pivotTable);
637
824
  }
638
- if (prop.pivotEntity && prop.pivotEntity !== prop.pivotTable) {
639
- this.entityImports.add(prop.pivotEntity);
640
- options.pivotEntity = `() => ${prop.pivotEntity}`;
825
+ if (prop.pivotEntity && Utils.className(prop.pivotEntity) !== prop.pivotTable) {
826
+ this.entityImports.add(Utils.className(prop.pivotEntity));
827
+ options.pivotEntity = `() => ${Utils.className(prop.pivotEntity)}`;
641
828
  }
642
829
  if (prop.joinColumns.length === 1) {
643
830
  options.joinColumn = this.quote(prop.joinColumns[0]);
644
831
  }
645
832
  else {
646
- options.joinColumns = `[${prop.joinColumns.map(this.quote).join(', ')}]`;
833
+ options.joinColumns = `[${prop.joinColumns.map(c => this.quote(c)).join(', ')}]`;
647
834
  }
648
835
  if (prop.inverseJoinColumns.length === 1) {
649
836
  options.inverseJoinColumn = this.quote(prop.inverseJoinColumns[0]);
650
837
  }
651
838
  else {
652
- options.inverseJoinColumns = `[${prop.inverseJoinColumns.map(this.quote).join(', ')}]`;
839
+ options.inverseJoinColumns = `[${prop.inverseJoinColumns.map(c => this.quote(c)).join(', ')}]`;
653
840
  }
654
841
  if (prop.fixedOrder) {
655
842
  options.fixedOrder = true;
@@ -672,7 +859,7 @@ export class SourceFile {
672
859
  if (prop.array) {
673
860
  options.array = true;
674
861
  }
675
- if (prop.object) {
862
+ if (prop.object && !prop.array) {
676
863
  options.object = true;
677
864
  }
678
865
  if (prop.prefix === false || typeof prop.prefix === 'string') {
@@ -698,17 +885,18 @@ export class SourceFile {
698
885
  }
699
886
  }
700
887
  else {
701
- if (prop.fieldNames.length > 1 && prop.fieldNames.some((fieldName, i) => fieldName !== this.namingStrategy.joinKeyColumnName(prop.name, prop.referencedColumnNames[i]))) {
888
+ if (prop.fieldNames.length > 1 &&
889
+ prop.fieldNames.some((fieldName, i) => fieldName !== this.namingStrategy.joinKeyColumnName(prop.name, prop.referencedColumnNames[i]))) {
702
890
  options.fieldNames = prop.fieldNames.map(fieldName => this.quote(fieldName));
703
891
  }
704
892
  }
705
893
  if (prop.ownColumns && prop.ownColumns.length !== prop.fieldNames.length) {
706
894
  options.referencedColumnNames = prop.referencedColumnNames.map(fieldName => this.quote(fieldName));
707
895
  }
708
- if (!['no action', 'restrict'].includes(prop.updateRule.toLowerCase())) {
896
+ if (prop.updateRule) {
709
897
  options.updateRule = this.quote(prop.updateRule);
710
898
  }
711
- if (!['no action', 'restrict'].includes(prop.deleteRule.toLowerCase())) {
899
+ if (prop.deleteRule) {
712
900
  options.deleteRule = this.quote(prop.deleteRule);
713
901
  }
714
902
  if (prop.primary) {
@@ -751,8 +939,10 @@ export class SourceFile {
751
939
  }
752
940
  referenceCoreImport(identifier) {
753
941
  this.coreImports.add(identifier);
754
- return this.options.coreImportsPrefix
755
- ? `${this.options.coreImportsPrefix}${identifier}`
756
- : identifier;
942
+ return this.options.coreImportsPrefix ? `${this.options.coreImportsPrefix}${identifier}` : identifier;
943
+ }
944
+ referenceDecoratorImport(identifier) {
945
+ this.decoratorImports.add(identifier);
946
+ return this.options.coreImportsPrefix ? `${this.options.coreImportsPrefix}${identifier}` : identifier;
757
947
  }
758
948
  }