@mikro-orm/entity-generator 7.0.0-dev.23 → 7.0.0-dev.231

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.
@@ -0,0 +1,5 @@
1
+ import { EntitySchemaSourceFile } from './EntitySchemaSourceFile.js';
2
+ export declare class DefineEntitySourceFile extends EntitySchemaSourceFile {
3
+ generate(): string;
4
+ private getPropertyBuilder;
5
+ }
@@ -0,0 +1,115 @@
1
+ import { Config, ReferenceKind, types, } from '@mikro-orm/core';
2
+ import { EntitySchemaSourceFile } from './EntitySchemaSourceFile.js';
3
+ export class DefineEntitySourceFile extends EntitySchemaSourceFile {
4
+ generate() {
5
+ const enumDefinitions = [];
6
+ for (const prop of Object.values(this.meta.properties)) {
7
+ if (prop.enum && (typeof prop.kind === 'undefined' || prop.kind === ReferenceKind.SCALAR)) {
8
+ const def = this.getEnumClassDefinition(prop, 2);
9
+ if (def.length) {
10
+ enumDefinitions.push(def);
11
+ }
12
+ }
13
+ }
14
+ let ret = '';
15
+ if (!this.options.inferEntityType) {
16
+ ret += this.generateClassDefinition() + '\n';
17
+ }
18
+ if (enumDefinitions.length) {
19
+ ret += enumDefinitions.join('\n') + '\n';
20
+ }
21
+ const entitySchemaOptions = {};
22
+ if (this.options.inferEntityType) {
23
+ entitySchemaOptions.name = this.quote(this.meta.className);
24
+ if (this.meta.compositePK) {
25
+ entitySchemaOptions.primaryKeys = this.meta.getPrimaryProps().map(p => this.quote(p.name));
26
+ }
27
+ }
28
+ else {
29
+ entitySchemaOptions.class = this.meta.className;
30
+ }
31
+ Object.assign(entitySchemaOptions, (this.meta.embeddable ? this.getEmbeddableDeclOptions() : (this.meta.collection ? this.getEntityDeclOptions() : {})));
32
+ const nameSuffix = this.options.inferEntityType ? '' : 'Schema';
33
+ const declLine = `export const ${this.meta.className + nameSuffix} = ${this.referenceCoreImport('defineEntity')}(`;
34
+ ret += declLine;
35
+ if (this.meta.indexes.length > 0) {
36
+ entitySchemaOptions.indexes = this.meta.indexes.map(index => this.getIndexOptions(index));
37
+ }
38
+ if (this.meta.uniques.length > 0) {
39
+ entitySchemaOptions.uniques = this.meta.uniques.map(index => this.getUniqueOptions(index));
40
+ }
41
+ entitySchemaOptions.properties = Object.fromEntries(Object.entries(this.meta.properties).map(([name, prop]) => [name, this.getPropertyBuilder(prop)]));
42
+ // Force top level and properties to be indented, regardless of line length
43
+ entitySchemaOptions[Config] = true;
44
+ entitySchemaOptions.properties[Config] = true;
45
+ ret += this.serializeObject(entitySchemaOptions, declLine.length > 80 ? undefined : 80 - declLine.length, 0);
46
+ ret += ');\n';
47
+ if (this.options.inferEntityType) {
48
+ ret += `\nexport interface I${this.meta.className} extends ${this.referenceCoreImport('InferEntity')}<typeof ${this.meta.className}> {}\n`;
49
+ }
50
+ ret = `${this.generateImports()}\n\n${ret}`;
51
+ return ret;
52
+ }
53
+ getPropertyBuilder(prop) {
54
+ const options = this.getPropertyOptions(prop, false);
55
+ const p = this.referenceCoreImport('p');
56
+ let builder = '';
57
+ switch (prop.kind) {
58
+ case ReferenceKind.ONE_TO_ONE:
59
+ builder += `() => ${p}.oneToOne(${prop.type})`;
60
+ break;
61
+ case ReferenceKind.ONE_TO_MANY:
62
+ builder += `() => ${p}.oneToMany(${prop.type})`;
63
+ break;
64
+ case ReferenceKind.MANY_TO_ONE:
65
+ builder += `() => ${p}.manyToOne(${prop.type})`;
66
+ break;
67
+ case ReferenceKind.MANY_TO_MANY:
68
+ builder += `() => ${p}.manyToMany(${prop.type})`;
69
+ break;
70
+ case ReferenceKind.EMBEDDED:
71
+ builder += `() => ${p}.embedded(${prop.type})`;
72
+ break;
73
+ case ReferenceKind.SCALAR:
74
+ default: {
75
+ if (options.type && !(options.type in types)) {
76
+ builder += `${p}.type(${options.type})`;
77
+ }
78
+ else {
79
+ builder += options.type ? `${p}.${options.type}()` : p;
80
+ }
81
+ }
82
+ }
83
+ const simpleOptions = new Set([
84
+ 'primary', 'ref', 'nullable', 'array', 'object', 'mapToPk', 'hidden', 'concurrencyCheck', 'lazy', 'eager',
85
+ 'orphanRemoval', 'version', 'unsigned', 'returning', 'createForeignKeyConstraint', 'fixedOrder', 'owner',
86
+ 'getter', 'setter', 'unique', 'index', 'hydrate', 'persist', 'autoincrement',
87
+ ]);
88
+ const skipOptions = new Set(['entity', 'kind', 'type', 'items']);
89
+ const spreadOptions = new Set([
90
+ 'fieldNames', 'joinColumns', 'inverseJoinColumns', 'referencedColumnNames', 'ownColumns', 'columnTypes',
91
+ 'cascade', 'ignoreSchemaChanges', 'customOrder', 'groups', 'where', 'orderBy',
92
+ ]);
93
+ const rename = {
94
+ fieldName: 'name',
95
+ };
96
+ for (const key of Object.keys(options)) {
97
+ if (typeof options[key] === 'undefined' || skipOptions.has(key)) {
98
+ continue;
99
+ }
100
+ const method = rename[key] ?? key;
101
+ const params = simpleOptions.has(key) && options[key] === true ? '' : options[key];
102
+ builder += `.${method}`;
103
+ if (key === 'enum') {
104
+ builder += `(${options.items})`;
105
+ }
106
+ else if (spreadOptions.has(key) && typeof params === 'string' && params.startsWith('[')) {
107
+ builder += `(${params.slice(1, -1)})`;
108
+ }
109
+ else {
110
+ builder += `(${params})`;
111
+ }
112
+ }
113
+ return builder;
114
+ }
115
+ }
@@ -1,5 +1,5 @@
1
1
  import { type GenerateOptions, type MikroORM } from '@mikro-orm/core';
2
- import { type EntityManager } from '@mikro-orm/knex';
2
+ import { type EntityManager } from '@mikro-orm/sql';
3
3
  export declare class EntityGenerator {
4
4
  private readonly em;
5
5
  private readonly config;
@@ -14,6 +14,7 @@ export declare class EntityGenerator {
14
14
  static register(orm: MikroORM): void;
15
15
  generate(options?: GenerateOptions): Promise<string[]>;
16
16
  private getEntityMetadata;
17
+ private cleanUpReferentialIntegrityRules;
17
18
  private matchName;
18
19
  private detectManyToManyRelations;
19
20
  private generateBidirectionalRelations;
@@ -1,8 +1,11 @@
1
- import { EntityMetadata, ReferenceKind, types, Utils, } from '@mikro-orm/core';
2
- import { DatabaseSchema, } from '@mikro-orm/knex';
1
+ import { EntitySchema, ReferenceKind, types, Utils, } from '@mikro-orm/core';
2
+ import { DatabaseSchema, } from '@mikro-orm/sql';
3
+ import { fs } from '@mikro-orm/core/fs-utils';
3
4
  import { dirname, join } from 'node:path';
4
5
  import { writeFile } from 'node:fs/promises';
6
+ import { DefineEntitySourceFile } from './DefineEntitySourceFile.js';
5
7
  import { EntitySchemaSourceFile } from './EntitySchemaSourceFile.js';
8
+ import { NativeEnumSourceFile } from './NativeEnumSourceFile.js';
6
9
  import { SourceFile } from './SourceFile.js';
7
10
  export class EntityGenerator {
8
11
  em;
@@ -31,29 +34,41 @@ export class EntityGenerator {
31
34
  const schema = await DatabaseSchema.create(this.connection, this.platform, this.config, undefined, undefined, options.takeTables, options.skipTables);
32
35
  const metadata = await this.getEntityMetadata(schema, options);
33
36
  const defaultPath = `${this.config.get('baseDir')}/generated-entities`;
34
- const baseDir = Utils.normalizePath(options.path ?? defaultPath);
37
+ const baseDir = fs.normalizePath(options.path ?? defaultPath);
38
+ this.sources.length = 0;
39
+ const map = {
40
+ defineEntity: DefineEntitySourceFile,
41
+ entitySchema: EntitySchemaSourceFile,
42
+ decorators: SourceFile,
43
+ };
44
+ if (options.entityDefinition !== 'decorators') {
45
+ options.scalarTypeInDecorator = true;
46
+ }
35
47
  for (const meta of metadata) {
36
- if (!meta.pivotTable || options.outputPurePivotTables || this.referencedEntities.has(meta)) {
37
- if (options.entitySchema) {
38
- this.sources.push(new EntitySchemaSourceFile(meta, this.namingStrategy, this.platform, { ...options, scalarTypeInDecorator: true }));
39
- }
40
- else {
41
- this.sources.push(new SourceFile(meta, this.namingStrategy, this.platform, options));
42
- }
48
+ if (meta.pivotTable && !options.outputPurePivotTables && !this.referencedEntities.has(meta)) {
49
+ continue;
43
50
  }
51
+ this.sources.push(new map[options.entityDefinition](meta, this.namingStrategy, this.platform, options));
52
+ }
53
+ for (const nativeEnum of Object.values(schema.getNativeEnums())) {
54
+ this.sources.push(new NativeEnumSourceFile({}, this.namingStrategy, this.platform, options, nativeEnum));
44
55
  }
56
+ const files = this.sources.map(file => [file.getBaseName(), file.generate()]);
45
57
  if (options.save) {
46
- Utils.ensureDir(baseDir);
47
- await Promise.all(this.sources.map(async (file) => {
48
- const fileName = file.getBaseName();
49
- const fileDir = dirname(fileName);
50
- if (fileDir !== '.') {
51
- Utils.ensureDir(join(baseDir, fileDir));
52
- }
53
- return writeFile(join(baseDir, fileName), file.generate(), { flush: true });
54
- }));
58
+ fs.ensureDir(baseDir);
59
+ const promises = [];
60
+ for (const [fileName, data] of files) {
61
+ promises.push((async () => {
62
+ const fileDir = dirname(fileName);
63
+ if (fileDir !== '.') {
64
+ fs.ensureDir(join(baseDir, fileDir));
65
+ }
66
+ await writeFile(join(baseDir, fileName), data, { flush: true });
67
+ })());
68
+ }
69
+ await Promise.all(promises);
55
70
  }
56
- return this.sources.map(file => file.generate());
71
+ return files.map(([, data]) => data);
57
72
  }
58
73
  async getEntityMetadata(schema, options) {
59
74
  const metadata = schema.getTables()
@@ -104,6 +119,7 @@ export class EntityGenerator {
104
119
  }
105
120
  }
106
121
  this.detectManyToManyRelations(metadata, options.onlyPurePivotTables, options.readOnlyPivotTables, options.outputPurePivotTables);
122
+ this.cleanUpReferentialIntegrityRules(metadata);
107
123
  if (options.bidirectionalRelations) {
108
124
  this.generateBidirectionalRelations(metadata, options.outputPurePivotTables);
109
125
  }
@@ -119,6 +135,60 @@ export class EntityGenerator {
119
135
  await options.onProcessedMetadata?.(metadata, this.platform);
120
136
  return metadata;
121
137
  }
138
+ cleanUpReferentialIntegrityRules(metadata) {
139
+ // Clear FK rules that match defaults for:
140
+ // 1. FK-as-PK entities (all PKs are FKs) - cascade for both update and delete
141
+ // 2. Fixed-order pivot tables (autoincrement id + 2 FK relations only) - cascade for both
142
+ // 3. Relations to composite PK targets - cascade for update
143
+ for (const meta of metadata) {
144
+ const pks = meta.getPrimaryProps();
145
+ const fkPks = pks.filter(pk => pk.kind !== ReferenceKind.SCALAR);
146
+ // Case 1: All PKs are FKs - default is cascade for both update and delete
147
+ if (fkPks.length > 0 && fkPks.length === pks.length) {
148
+ for (const pk of fkPks) {
149
+ if (pk.deleteRule === 'cascade') {
150
+ delete pk.deleteRule;
151
+ }
152
+ if (pk.updateRule === 'cascade') {
153
+ delete pk.updateRule;
154
+ }
155
+ }
156
+ }
157
+ // Case 2: Fixed-order pivot table (single autoincrement id PK + exactly 2 M:1 relations)
158
+ const hasAutoIncrementPk = pks.length === 1 && pks[0].autoincrement;
159
+ const m2oRelations = meta.relations.filter(r => r.kind === ReferenceKind.MANY_TO_ONE);
160
+ if (hasAutoIncrementPk && m2oRelations.length === 2) {
161
+ // Check if all columns are either the PK or FK columns
162
+ const fkColumns = new Set(m2oRelations.flatMap(r => r.fieldNames));
163
+ const pkColumns = new Set(pks.flatMap(p => p.fieldNames));
164
+ const allColumns = new Set(meta.props.filter(p => p.persist !== false).flatMap(p => p.fieldNames));
165
+ const isPivotLike = [...allColumns].every(col => fkColumns.has(col) || pkColumns.has(col));
166
+ if (isPivotLike) {
167
+ for (const rel of m2oRelations) {
168
+ if (rel.updateRule === 'cascade') {
169
+ delete rel.updateRule;
170
+ }
171
+ if (rel.deleteRule === 'cascade') {
172
+ delete rel.deleteRule;
173
+ }
174
+ }
175
+ }
176
+ }
177
+ // Case 3: Relations to composite PK targets - default is cascade for update
178
+ // Case 4: Nullable relations - default is set null for delete
179
+ for (const rel of meta.relations) {
180
+ if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(rel.kind)) {
181
+ const targetMeta = metadata.find(m => m.className === rel.type);
182
+ if (targetMeta?.compositePK && rel.updateRule === 'cascade') {
183
+ delete rel.updateRule;
184
+ }
185
+ if (rel.nullable && rel.deleteRule === 'set null') {
186
+ delete rel.deleteRule;
187
+ }
188
+ }
189
+ }
190
+ }
191
+ }
122
192
  matchName(name, nameToMatch) {
123
193
  return typeof nameToMatch === 'string'
124
194
  ? name.toLocaleLowerCase() === nameToMatch.toLocaleLowerCase()
@@ -189,8 +259,19 @@ export class EntityGenerator {
189
259
  }
190
260
  }
191
261
  meta.pivotTable = true;
262
+ // Clear FK rules that match the default for pivot tables (cascade)
263
+ // so they don't get output explicitly in generated code
264
+ for (const rel of meta.relations) {
265
+ if (rel.updateRule === 'cascade') {
266
+ delete rel.updateRule;
267
+ }
268
+ if (rel.deleteRule === 'cascade') {
269
+ delete rel.deleteRule;
270
+ }
271
+ }
192
272
  const owner = metadata.find(m => m.className === meta.relations[0].type);
193
- const name = this.namingStrategy.columnNameToProperty(meta.tableName.replace(new RegExp('^' + owner.tableName + '_'), ''));
273
+ const target = metadata.find(m => m.className === meta.relations[1].type);
274
+ const name = this.namingStrategy.manyToManyPropertyName(owner.className, target.className, meta.tableName, owner.tableName, meta.schema);
194
275
  const ownerProp = {
195
276
  name,
196
277
  kind: ReferenceKind.MANY_TO_MANY,
@@ -200,7 +281,7 @@ export class EntityGenerator {
200
281
  inverseJoinColumns: meta.relations[1].fieldNames,
201
282
  };
202
283
  if (outputPurePivotTables || this.referencedEntities.has(meta)) {
203
- ownerProp.pivotEntity = meta.className;
284
+ ownerProp.pivotEntity = meta.class;
204
285
  }
205
286
  if (fixedOrderColumn) {
206
287
  ownerProp.fixedOrder = true;
@@ -264,20 +345,16 @@ export class EntityGenerator {
264
345
  }
265
346
  }
266
347
  generateAndAttachCustomBaseEntity(metadata, customBaseEntityName) {
267
- let baseClassExists = false;
348
+ let base = metadata.find(meta => meta.className === customBaseEntityName);
349
+ if (!base) {
350
+ const schema = new EntitySchema({ className: customBaseEntityName, abstract: true });
351
+ base = schema.init().meta;
352
+ metadata.push(base);
353
+ }
268
354
  for (const meta of metadata) {
269
- if (meta.className === customBaseEntityName) {
270
- baseClassExists = true;
271
- continue;
355
+ if (meta.className !== customBaseEntityName) {
356
+ meta.extends ??= base.class;
272
357
  }
273
- meta.extends ??= customBaseEntityName;
274
- }
275
- if (!baseClassExists) {
276
- metadata.push(new EntityMetadata({
277
- className: customBaseEntityName,
278
- abstract: true,
279
- relations: [],
280
- }));
281
358
  }
282
359
  }
283
360
  castNullDefaultsToUndefined(metadata) {
@@ -2,7 +2,8 @@ import { type Dictionary, type EntityProperty } from '@mikro-orm/core';
2
2
  import { SourceFile } from './SourceFile.js';
3
3
  export declare class EntitySchemaSourceFile extends SourceFile {
4
4
  generate(): string;
5
- private getPropertyOptions;
5
+ protected generateClassDefinition(): string;
6
+ protected getPropertyOptions(prop: EntityProperty, quote?: boolean): Dictionary;
6
7
  protected getPropertyIndexesOptions(prop: EntityProperty, options: Dictionary): void;
7
- protected getScalarPropertyDecoratorOptions(options: Dictionary, prop: EntityProperty): void;
8
+ protected getScalarPropertyDecoratorOptions(options: Dictionary, prop: EntityProperty, quote?: boolean): void;
8
9
  }
@@ -2,21 +2,56 @@ import { Config, ReferenceKind, } from '@mikro-orm/core';
2
2
  import { SourceFile } from './SourceFile.js';
3
3
  export class EntitySchemaSourceFile extends SourceFile {
4
4
  generate() {
5
+ const classDefinition = this.generateClassDefinition();
6
+ const enumDefinitions = [];
7
+ for (const prop of Object.values(this.meta.properties)) {
8
+ if (prop.enum && (typeof prop.kind === 'undefined' || prop.kind === ReferenceKind.SCALAR)) {
9
+ const def = this.getEnumClassDefinition(prop, 2);
10
+ if (def.length) {
11
+ enumDefinitions.push(def);
12
+ }
13
+ }
14
+ }
15
+ let ret = classDefinition;
16
+ if (enumDefinitions.length) {
17
+ ret += '\n' + enumDefinitions.join('\n');
18
+ }
19
+ ret += `\n`;
20
+ const entitySchemaOptions = {
21
+ class: this.meta.className,
22
+ ...(this.meta.embeddable ? this.getEmbeddableDeclOptions() : (this.meta.collection ? this.getEntityDeclOptions() : {})),
23
+ };
24
+ const declLine = `export const ${this.meta.className}Schema = new ${this.referenceCoreImport('EntitySchema')}(`;
25
+ ret += declLine;
26
+ if (this.meta.indexes.length > 0) {
27
+ entitySchemaOptions.indexes = this.meta.indexes.map(index => this.getIndexOptions(index));
28
+ }
29
+ if (this.meta.uniques.length > 0) {
30
+ entitySchemaOptions.uniques = this.meta.uniques.map(index => this.getUniqueOptions(index));
31
+ }
32
+ entitySchemaOptions.properties = Object.fromEntries(Object.entries(this.meta.properties).map(([name, prop]) => [name, this.getPropertyOptions(prop)]));
33
+ // Force top level and properties to be indented, regardless of line length
34
+ entitySchemaOptions[Config] = true;
35
+ entitySchemaOptions.properties[Config] = true;
36
+ ret += this.serializeObject(entitySchemaOptions, declLine.length > 80 ? undefined : 80 - declLine.length, 0);
37
+ ret += ');\n';
38
+ ret = `${this.generateImports()}\n\n${ret}`;
39
+ return ret;
40
+ }
41
+ generateClassDefinition() {
5
42
  let classBody = '';
6
- if (this.meta.className === this.options.customBaseEntityName) {
43
+ if (!this.options.customBaseEntityName || this.meta.className === this.options.customBaseEntityName) {
7
44
  const defineConfigTypeSettings = {};
8
45
  defineConfigTypeSettings.forceObject = this.platform.getConfig().get('serialization').forceObject ?? false;
9
- classBody += `${' '.repeat(2)}[${this.referenceCoreImport('Config')}]?: ${this.referenceCoreImport('DefineConfig')}<${this.serializeObject(defineConfigTypeSettings)}>;\n`;
46
+ if (defineConfigTypeSettings.forceObject) {
47
+ classBody += `${' '.repeat(2)}[${this.referenceCoreImport('Config')}]?: ${this.referenceCoreImport('DefineConfig')}<${this.serializeObject(defineConfigTypeSettings)}>;\n`;
48
+ }
10
49
  }
11
- const enumDefinitions = [];
12
50
  const eagerProperties = [];
13
51
  const primaryProps = [];
14
52
  const props = [];
15
53
  for (const prop of Object.values(this.meta.properties)) {
16
54
  props.push(this.getPropertyDefinition(prop, 2));
17
- if (prop.enum && (typeof prop.kind === 'undefined' || prop.kind === ReferenceKind.SCALAR)) {
18
- enumDefinitions.push(this.getEnumClassDefinition(prop, 2));
19
- }
20
55
  if (prop.eager) {
21
56
  eagerProperties.push(prop);
22
57
  }
@@ -38,33 +73,9 @@ export class EntitySchemaSourceFile extends SourceFile {
38
73
  classBody += `${' '.repeat(2)}[${this.referenceCoreImport('EagerProps')}]?: ${eagerPropertyNames.join(' | ')};\n`;
39
74
  }
40
75
  classBody += `${props.join('')}`;
41
- let ret = this.getEntityClass(classBody);
42
- if (enumDefinitions.length) {
43
- ret += '\n' + enumDefinitions.join('\n');
44
- }
45
- ret += `\n`;
46
- const entitySchemaOptions = {
47
- class: this.meta.className,
48
- ...(this.meta.embeddable ? this.getEmbeddableDeclOptions() : (this.meta.collection ? this.getEntityDeclOptions() : {})),
49
- };
50
- const declLine = `export const ${this.meta.className}Schema = new ${this.referenceCoreImport('EntitySchema')}(`;
51
- ret += declLine;
52
- if (this.meta.indexes.length > 0) {
53
- entitySchemaOptions.indexes = this.meta.indexes.map(index => this.getIndexOptions(index));
54
- }
55
- if (this.meta.uniques.length > 0) {
56
- entitySchemaOptions.uniques = this.meta.uniques.map(index => this.getUniqueOptions(index));
57
- }
58
- entitySchemaOptions.properties = Object.fromEntries(Object.entries(this.meta.properties).map(([name, prop]) => [name, this.getPropertyOptions(prop)]));
59
- // Force top level and properties to be indented, regardless of line length
60
- entitySchemaOptions[Config] = true;
61
- entitySchemaOptions.properties[Config] = true;
62
- ret += this.serializeObject(entitySchemaOptions, declLine.length > 80 ? undefined : 80 - declLine.length, 0);
63
- ret += ');\n';
64
- ret = `${this.generateImports()}\n\n${ret}`;
65
- return ret;
76
+ return this.getEntityClass(classBody);
66
77
  }
67
- getPropertyOptions(prop) {
78
+ getPropertyOptions(prop, quote = true) {
68
79
  const options = {};
69
80
  if (prop.primary) {
70
81
  options.primary = true;
@@ -79,7 +90,7 @@ export class EntitySchemaSourceFile extends SourceFile {
79
90
  this.getOneToManyDecoratorOptions(options, prop);
80
91
  }
81
92
  else if (prop.kind === ReferenceKind.SCALAR || typeof prop.kind === 'undefined') {
82
- this.getScalarPropertyDecoratorOptions(options, prop);
93
+ this.getScalarPropertyDecoratorOptions(options, prop, quote);
83
94
  }
84
95
  else if (prop.kind === ReferenceKind.EMBEDDED) {
85
96
  this.getEmbeddedPropertyDeclarationOptions(options, prop);
@@ -123,14 +134,14 @@ export class EntitySchemaSourceFile extends SourceFile {
123
134
  processIndex('index');
124
135
  processIndex('unique');
125
136
  }
126
- getScalarPropertyDecoratorOptions(options, prop) {
137
+ getScalarPropertyDecoratorOptions(options, prop, quote = true) {
127
138
  if (prop.enum) {
128
139
  options.enum = true;
129
140
  options.items = `() => ${prop.runtimeType}`;
130
141
  }
131
142
  else {
132
- options.type = this.quote(prop.type);
143
+ options.type = quote ? this.quote(prop.type) : prop.type;
133
144
  }
134
- super.getScalarPropertyDecoratorOptions(options, prop);
145
+ super.getScalarPropertyDecoratorOptions(options, prop, quote);
135
146
  }
136
147
  }
@@ -0,0 +1,16 @@
1
+ import type { EntityMetadata, GenerateOptions, NamingStrategy, Platform } from '@mikro-orm/core';
2
+ import { SourceFile } from './SourceFile.js';
3
+ export declare class NativeEnumSourceFile extends SourceFile {
4
+ protected readonly nativeEnum: {
5
+ name: string;
6
+ schema?: string;
7
+ items: string[];
8
+ };
9
+ constructor(meta: EntityMetadata, namingStrategy: NamingStrategy, platform: Platform, options: GenerateOptions, nativeEnum: {
10
+ name: string;
11
+ schema?: string;
12
+ items: string[];
13
+ });
14
+ generate(): string;
15
+ getBaseName(extension?: string): string;
16
+ }
@@ -0,0 +1,47 @@
1
+ import { identifierRegex, SourceFile } from './SourceFile.js';
2
+ export class NativeEnumSourceFile extends SourceFile {
3
+ nativeEnum;
4
+ constructor(meta, namingStrategy, platform, options, nativeEnum) {
5
+ super(meta, namingStrategy, platform, options);
6
+ this.nativeEnum = nativeEnum;
7
+ }
8
+ generate() {
9
+ const enumClassName = this.namingStrategy.getEnumClassName(this.nativeEnum.name, undefined, this.nativeEnum.schema);
10
+ const enumTypeName = this.namingStrategy.getEnumTypeName(this.nativeEnum.name, undefined, this.nativeEnum.schema);
11
+ const padding = ' ';
12
+ const enumMode = this.options.enumMode;
13
+ const enumValues = this.nativeEnum.items;
14
+ if (enumMode === 'union-type') {
15
+ return `export type ${enumTypeName} = ${enumValues.map(item => this.quote(item)).join(' | ')};\n`;
16
+ }
17
+ let ret = '';
18
+ if (enumMode === 'dictionary') {
19
+ ret += `export const ${enumClassName} = {\n`;
20
+ }
21
+ else {
22
+ ret += `export enum ${enumClassName} {\n`;
23
+ }
24
+ for (const enumValue of enumValues) {
25
+ const enumName = this.namingStrategy.enumValueToEnumProperty(enumValue, this.nativeEnum.name, '', this.nativeEnum.schema);
26
+ if (enumMode === 'dictionary') {
27
+ ret += `${padding}${identifierRegex.test(enumName) ? enumName : this.quote(enumName)}: ${this.quote(enumValue)},\n`;
28
+ }
29
+ else {
30
+ ret += `${padding}${identifierRegex.test(enumName) ? enumName : this.quote(enumName)} = ${this.quote(enumValue)},\n`;
31
+ }
32
+ }
33
+ if (enumMode === 'dictionary') {
34
+ ret += '} as const;\n';
35
+ }
36
+ else {
37
+ ret += '}\n';
38
+ }
39
+ if (enumMode === 'dictionary') {
40
+ ret += `\nexport type ${enumTypeName} = (typeof ${enumClassName})[keyof typeof ${enumClassName}];\n`;
41
+ }
42
+ return ret;
43
+ }
44
+ getBaseName(extension = '.ts') {
45
+ return `${this.options.fileName(this.nativeEnum.name)}${extension}`;
46
+ }
47
+ }
package/README.md CHANGED
@@ -381,6 +381,8 @@ See also the list of contributors who [participated](https://github.com/mikro-or
381
381
 
382
382
  Please ⭐️ this repository if this project helped you!
383
383
 
384
+ > If you'd like to support my open-source work, consider sponsoring me directly at [github.com/sponsors/b4nan](https://github.com/sponsors/b4nan).
385
+
384
386
  ## 📝 License
385
387
 
386
388
  Copyright © 2018 [Martin Adámek](https://github.com/b4nan).
package/SourceFile.d.ts CHANGED
@@ -9,7 +9,9 @@ export declare class SourceFile {
9
9
  protected readonly platform: Platform;
10
10
  protected readonly options: GenerateOptions;
11
11
  protected readonly coreImports: Set<string>;
12
+ protected readonly decoratorImports: Set<string>;
12
13
  protected readonly entityImports: Set<string>;
14
+ protected readonly enumImports: Map<string, string[]>;
13
15
  constructor(meta: EntityMetadata, namingStrategy: NamingStrategy, platform: Platform, options: GenerateOptions);
14
16
  generate(): string;
15
17
  protected getIndexOptions(index: EntityMetadata['indexes'][number], isAtEntityLevel?: boolean): IndexOptions<Dictionary, string>;
@@ -30,11 +32,12 @@ export declare class SourceFile {
30
32
  protected getCommonDecoratorOptions(options: Dictionary, prop: EntityProperty): void;
31
33
  private propTypeBreakdowns;
32
34
  private breakdownOfIType;
33
- protected getScalarPropertyDecoratorOptions(options: Dictionary, prop: EntityProperty): void;
35
+ protected getScalarPropertyDecoratorOptions(options: Dictionary, prop: EntityProperty, quote?: boolean): void;
34
36
  protected getManyToManyDecoratorOptions(options: Dictionary, prop: EntityProperty): void;
35
37
  protected getOneToManyDecoratorOptions(options: Dictionary, prop: EntityProperty): void;
36
38
  protected getEmbeddedPropertyDeclarationOptions(options: Dictionary, prop: EntityProperty): void;
37
39
  protected getForeignKeyDecoratorOptions(options: OneToOneOptions<any, any>, prop: EntityProperty): void;
38
40
  protected getDecoratorType(prop: EntityProperty): string;
39
41
  protected referenceCoreImport(identifier: string): string;
42
+ protected referenceDecoratorImport(identifier: string): string;
40
43
  }