@mikro-orm/entity-generator 7.0.0-dev.39 → 7.0.0-dev.40

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
+ }
@@ -2,7 +2,9 @@ import { EntityMetadata, ReferenceKind, types, Utils, } from '@mikro-orm/core';
2
2
  import { DatabaseSchema, } from '@mikro-orm/knex';
3
3
  import { dirname, join } from 'node:path';
4
4
  import { writeFile } from 'node:fs/promises';
5
+ import { DefineEntitySourceFile } from './DefineEntitySourceFile.js';
5
6
  import { EntitySchemaSourceFile } from './EntitySchemaSourceFile.js';
7
+ import { NativeEnumSourceFile } from './NativeEnumSourceFile.js';
6
8
  import { SourceFile } from './SourceFile.js';
7
9
  export class EntityGenerator {
8
10
  em;
@@ -32,28 +34,43 @@ export class EntityGenerator {
32
34
  const metadata = await this.getEntityMetadata(schema, options);
33
35
  const defaultPath = `${this.config.get('baseDir')}/generated-entities`;
34
36
  const baseDir = Utils.normalizePath(options.path ?? defaultPath);
37
+ this.sources.length = 0;
38
+ if (options.entitySchema) {
39
+ options.entityDefinition = 'entitySchema';
40
+ }
41
+ const map = {
42
+ defineEntity: DefineEntitySourceFile,
43
+ entitySchema: EntitySchemaSourceFile,
44
+ decorators: SourceFile,
45
+ };
46
+ if (options.entityDefinition !== 'decorators') {
47
+ options.scalarTypeInDecorator = true;
48
+ }
35
49
  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
- }
50
+ if (meta.pivotTable && !options.outputPurePivotTables && !this.referencedEntities.has(meta)) {
51
+ continue;
43
52
  }
53
+ this.sources.push(new map[options.entityDefinition](meta, this.namingStrategy, this.platform, options));
44
54
  }
55
+ for (const nativeEnum of Object.values(schema.getNativeEnums())) {
56
+ this.sources.push(new NativeEnumSourceFile({}, this.namingStrategy, this.platform, options, nativeEnum));
57
+ }
58
+ const files = this.sources.map(file => [file.getBaseName(), file.generate()]);
45
59
  if (options.save) {
46
60
  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
- }));
61
+ const promises = [];
62
+ for (const [fileName, data] of files) {
63
+ promises.push((async () => {
64
+ const fileDir = dirname(fileName);
65
+ if (fileDir !== '.') {
66
+ Utils.ensureDir(join(baseDir, fileDir));
67
+ }
68
+ await writeFile(join(baseDir, fileName), data, { flush: true });
69
+ })());
70
+ }
71
+ await Promise.all(promises);
55
72
  }
56
- return this.sources.map(file => file.generate());
73
+ return files.map(([, data]) => data);
57
74
  }
58
75
  async getEntityMetadata(schema, options) {
59
76
  const metadata = schema.getTables()
@@ -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
@@ -10,6 +10,7 @@ export declare class SourceFile {
10
10
  protected readonly options: GenerateOptions;
11
11
  protected readonly coreImports: Set<string>;
12
12
  protected readonly entityImports: Set<string>;
13
+ protected readonly enumImports: Map<string, string[]>;
13
14
  constructor(meta: EntityMetadata, namingStrategy: NamingStrategy, platform: Platform, options: GenerateOptions);
14
15
  generate(): string;
15
16
  protected getIndexOptions(index: EntityMetadata['indexes'][number], isAtEntityLevel?: boolean): IndexOptions<Dictionary, string>;
@@ -30,7 +31,7 @@ export declare class SourceFile {
30
31
  protected getCommonDecoratorOptions(options: Dictionary, prop: EntityProperty): void;
31
32
  private propTypeBreakdowns;
32
33
  private breakdownOfIType;
33
- protected getScalarPropertyDecoratorOptions(options: Dictionary, prop: EntityProperty): void;
34
+ protected getScalarPropertyDecoratorOptions(options: Dictionary, prop: EntityProperty, quote?: boolean): void;
34
35
  protected getManyToManyDecoratorOptions(options: Dictionary, prop: EntityProperty): void;
35
36
  protected getOneToManyDecoratorOptions(options: Dictionary, prop: EntityProperty): void;
36
37
  protected getEmbeddedPropertyDeclarationOptions(options: Dictionary, prop: EntityProperty): void;
package/SourceFile.js CHANGED
@@ -14,6 +14,7 @@ export class SourceFile {
14
14
  options;
15
15
  coreImports = 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;
@@ -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);
@@ -146,32 +150,45 @@ export class SourceFile {
146
150
  const basePath = relative(dir, this.options.path ?? '.') || '.';
147
151
  (this.options.extraImports?.(basePath, base) ?? []).forEach(v => this.entityImports.add(v));
148
152
  const entityImports = [...this.entityImports].filter(e => e !== this.meta.className);
149
- entityImports.sort().forEach(entity => {
153
+ const importMap = new Map();
154
+ for (const entity of entityImports) {
150
155
  const file = this.options.onImport?.(entity, basePath, extension, base) ?? {
151
156
  path: `${basePath}/${this.options.fileName(entity)}${extension}`,
152
157
  name: entity,
153
158
  };
154
159
  if (file.path === '') {
155
160
  if (file.name === '') {
156
- return;
161
+ continue;
157
162
  }
158
- imports.add(`import ${this.quote(file.name)};`);
159
- return;
163
+ importMap.set(file.path, `import ${this.quote(file.name)};`);
164
+ continue;
160
165
  }
161
166
  if (file.name === '') {
162
- imports.add(`import * as ${entity} from ${this.quote(file.path)};`);
163
- return;
167
+ importMap.set(file.path, `import * as ${entity} from ${this.quote(file.path)};`);
168
+ continue;
164
169
  }
165
170
  if (file.name === 'default') {
166
- imports.add(`import ${entity} from ${this.quote(file.path)};`);
167
- return;
171
+ importMap.set(file.path, `import ${entity} from ${this.quote(file.path)};`);
172
+ continue;
168
173
  }
169
174
  if (file.name === entity) {
170
- imports.add(`import { ${entity} } from ${this.quote(file.path)};`);
171
- return;
175
+ importMap.set(file.path, `import { ${entity} } from ${this.quote(file.path)};`);
176
+ continue;
172
177
  }
173
- imports.add(`import { ${identifierRegex.test(file.name) ? file.name : this.quote(file.name)} as ${entity} } from ${this.quote(file.path)};`);
174
- });
178
+ importMap.set(file.path, `import { ${identifierRegex.test(file.name) ? file.name : this.quote(file.name)} as ${entity} } from ${this.quote(file.path)};`);
179
+ }
180
+ if (this.enumImports.size) {
181
+ for (const [name, exports] of this.enumImports.entries()) {
182
+ const file = this.options.onImport?.(name, basePath, extension, base) ?? {
183
+ path: `${basePath}/${this.options.fileName(name)}${extension}`,
184
+ name,
185
+ };
186
+ importMap.set(file.path, `import { ${exports.join(', ')} } from ${this.quote(file.path)};`);
187
+ }
188
+ }
189
+ for (const key of [...importMap.keys()].sort()) {
190
+ imports.add(importMap.get(key));
191
+ }
175
192
  return Array.from(imports.values()).join('\n');
176
193
  }
177
194
  getEntityClass(classBody) {
@@ -201,6 +218,7 @@ export class SourceFile {
201
218
  getPropertyDefinition(prop, padLeft) {
202
219
  const padding = ' '.repeat(padLeft);
203
220
  const propName = identifierRegex.test(prop.name) ? prop.name : this.quote(prop.name);
221
+ const enumMode = this.options.enumMode;
204
222
  let hiddenType = '';
205
223
  if (prop.hidden) {
206
224
  hiddenType += ` & ${this.referenceCoreImport('Hidden')}`;
@@ -218,7 +236,11 @@ export class SourceFile {
218
236
  : (() => {
219
237
  if (isScalar) {
220
238
  if (prop.enum) {
221
- return prop.runtimeType;
239
+ const method = enumMode === 'ts-enum' ? 'getEnumClassName' : 'getEnumTypeName';
240
+ if (prop.nativeEnumName) {
241
+ return this.namingStrategy[method](prop.nativeEnumName, undefined, this.meta.schema);
242
+ }
243
+ return this.namingStrategy[method](prop.fieldNames[0], this.meta.collection, this.meta.schema);
222
244
  }
223
245
  breakdownOfIType = this.breakdownOfIType(prop);
224
246
  if (typeof breakdownOfIType !== 'undefined') {
@@ -253,8 +275,14 @@ export class SourceFile {
253
275
  return `${padding}${ret};\n`;
254
276
  }
255
277
  if (prop.enum && typeof prop.default === 'string') {
278
+ if (enumMode === 'union-type') {
279
+ return `${padding}${ret} = ${this.quote(prop.default)};\n`;
280
+ }
281
+ const enumClassName = prop.nativeEnumName
282
+ ? this.namingStrategy.getEnumClassName(prop.nativeEnumName, undefined, this.meta.schema)
283
+ : this.namingStrategy.getEnumClassName(prop.fieldNames[0], this.meta.collection, this.meta.schema);
256
284
  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`;
285
+ return `${padding}${ret} = ${enumClassName}${identifierRegex.test(enumVal) ? `.${enumVal}` : `[${this.quote(enumVal)}]`};\n`;
258
286
  }
259
287
  if (prop.fieldNames?.length > 1) {
260
288
  // TODO: Composite FKs with default values require additions to default/defaultRaw that are not yet supported.
@@ -270,15 +298,51 @@ export class SourceFile {
270
298
  return `${padding}${ret} = ${prop.ref ? this.referenceCoreImport('ref') : this.referenceCoreImport('rel')}(${propType}, ${defaultVal});\n`;
271
299
  }
272
300
  getEnumClassDefinition(prop, padLeft) {
301
+ const enumMode = this.options.enumMode;
302
+ if (prop.nativeEnumName) {
303
+ const imports = [];
304
+ if (enumMode !== 'union-type') {
305
+ imports.push(prop.runtimeType);
306
+ }
307
+ if (!this.options.inferEntityType && enumMode !== 'ts-enum') {
308
+ const enumTypeName = this.namingStrategy.getEnumTypeName(prop.nativeEnumName, undefined, this.meta.schema);
309
+ imports.push(enumTypeName);
310
+ }
311
+ this.enumImports.set(prop.runtimeType, imports);
312
+ return '';
313
+ }
273
314
  const enumClassName = this.namingStrategy.getEnumClassName(prop.fieldNames[0], this.meta.collection, this.meta.schema);
315
+ const enumTypeName = this.namingStrategy.getEnumTypeName(prop.fieldNames[0], this.meta.collection, this.meta.schema);
274
316
  const padding = ' '.repeat(padLeft);
275
- let ret = `export enum ${enumClassName} {\n`;
276
317
  const enumValues = prop.items;
318
+ if (enumMode === 'union-type') {
319
+ return `export type ${enumTypeName} = ${enumValues.map(item => this.quote(item)).join(' | ')};\n`;
320
+ }
321
+ let ret = '';
322
+ if (enumMode === 'dictionary') {
323
+ ret += `export const ${enumClassName} = {\n`;
324
+ }
325
+ else {
326
+ ret += `export enum ${enumClassName} {\n`;
327
+ }
277
328
  for (const enumValue of enumValues) {
278
329
  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`;
330
+ if (enumMode === 'dictionary') {
331
+ ret += `${padding}${identifierRegex.test(enumName) ? enumName : this.quote(enumName)}: ${this.quote(enumValue)},\n`;
332
+ }
333
+ else {
334
+ ret += `${padding}${identifierRegex.test(enumName) ? enumName : this.quote(enumName)} = ${this.quote(enumValue)},\n`;
335
+ }
336
+ }
337
+ if (enumMode === 'dictionary') {
338
+ ret += '} as const;\n';
339
+ }
340
+ else {
341
+ ret += '}\n';
342
+ }
343
+ if (enumMode === 'dictionary') {
344
+ ret += `\nexport type ${enumTypeName} = (typeof ${enumClassName})[keyof typeof ${enumClassName}];\n`;
280
345
  }
281
- ret += '}\n';
282
346
  return ret;
283
347
  }
284
348
  serializeObject(options, wordwrap, spaces, level = 0) {
@@ -537,12 +601,23 @@ export class SourceFile {
537
601
  this.propTypeBreakdowns.set(prop, r);
538
602
  return r;
539
603
  }
540
- getScalarPropertyDecoratorOptions(options, prop) {
604
+ getScalarPropertyDecoratorOptions(options, prop, quote = true) {
541
605
  if (prop.fieldNames[0] !== this.namingStrategy.propertyToColumnName(prop.name)) {
542
606
  options.fieldName = this.quote(prop.fieldNames[0]);
543
607
  }
544
608
  if (prop.enum) {
545
- options.items = `() => ${prop.runtimeType}`;
609
+ if (this.options.enumMode === 'union-type') {
610
+ options.items = `[${prop.items.map(item => this.quote(item)).join(', ')}]`;
611
+ }
612
+ else if (prop.nativeEnumName) {
613
+ const enumClassName = this.namingStrategy.getEnumClassName(prop.nativeEnumName, undefined, this.meta.schema);
614
+ options.items = `() => ${enumClassName}`;
615
+ options.nativeEnumName = this.quote(prop.nativeEnumName);
616
+ }
617
+ else {
618
+ const enumClassName = this.namingStrategy.getEnumClassName(prop.fieldNames[0], this.meta.collection, this.meta.schema);
619
+ options.items = `() => ${enumClassName}`;
620
+ }
546
621
  }
547
622
  // For enum properties, we don't need a column type
548
623
  // or the property length or other information in the decorator.
@@ -571,7 +646,7 @@ export class SourceFile {
571
646
  return ((useDefault && !hasUsableNullDefault) || (prop.optional && !prop.nullable));
572
647
  })() // also when there is the "| Opt" type modifier (because reflect-metadata can't extract the base)
573
648
  ) {
574
- options.type = this.quote(prop.type);
649
+ options.type = quote ? this.quote(prop.type) : prop.type;
575
650
  }
576
651
  }
577
652
  const columnTypeFromMappedRuntimeType = mappedRuntimeType.getColumnType({ ...prop, autoincrement: false }, this.platform);
@@ -672,7 +747,7 @@ export class SourceFile {
672
747
  if (prop.array) {
673
748
  options.array = true;
674
749
  }
675
- if (prop.object) {
750
+ if (prop.object && !prop.array) {
676
751
  options.object = true;
677
752
  }
678
753
  if (prop.prefix === false || typeof prop.prefix === 'string') {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mikro-orm/entity-generator",
3
3
  "type": "module",
4
- "version": "7.0.0-dev.39",
4
+ "version": "7.0.0-dev.40",
5
5
  "description": "TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JavaScript.",
6
6
  "exports": {
7
7
  "./package.json": "./package.json",
@@ -50,12 +50,12 @@
50
50
  "access": "public"
51
51
  },
52
52
  "dependencies": {
53
- "@mikro-orm/knex": "7.0.0-dev.39"
53
+ "@mikro-orm/knex": "7.0.0-dev.40"
54
54
  },
55
55
  "devDependencies": {
56
- "@mikro-orm/core": "^6.5.8"
56
+ "@mikro-orm/core": "^6.6.0"
57
57
  },
58
58
  "peerDependencies": {
59
- "@mikro-orm/core": "7.0.0-dev.39"
59
+ "@mikro-orm/core": "7.0.0-dev.40"
60
60
  }
61
61
  }