@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.
- package/DefineEntitySourceFile.d.ts +5 -0
- package/DefineEntitySourceFile.js +115 -0
- package/EntityGenerator.js +33 -16
- package/EntitySchemaSourceFile.d.ts +3 -2
- package/EntitySchemaSourceFile.js +47 -36
- package/NativeEnumSourceFile.d.ts +16 -0
- package/NativeEnumSourceFile.js +47 -0
- package/README.md +2 -0
- package/SourceFile.d.ts +2 -1
- package/SourceFile.js +97 -22
- package/package.json +4 -4
|
@@ -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
|
+
}
|
package/EntityGenerator.js
CHANGED
|
@@ -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 (
|
|
37
|
-
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
161
|
+
continue;
|
|
157
162
|
}
|
|
158
|
-
|
|
159
|
-
|
|
163
|
+
importMap.set(file.path, `import ${this.quote(file.name)};`);
|
|
164
|
+
continue;
|
|
160
165
|
}
|
|
161
166
|
if (file.name === '') {
|
|
162
|
-
|
|
163
|
-
|
|
167
|
+
importMap.set(file.path, `import * as ${entity} from ${this.quote(file.path)};`);
|
|
168
|
+
continue;
|
|
164
169
|
}
|
|
165
170
|
if (file.name === 'default') {
|
|
166
|
-
|
|
167
|
-
|
|
171
|
+
importMap.set(file.path, `import ${entity} from ${this.quote(file.path)};`);
|
|
172
|
+
continue;
|
|
168
173
|
}
|
|
169
174
|
if (file.name === entity) {
|
|
170
|
-
|
|
171
|
-
|
|
175
|
+
importMap.set(file.path, `import { ${entity} } from ${this.quote(file.path)};`);
|
|
176
|
+
continue;
|
|
172
177
|
}
|
|
173
|
-
|
|
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
|
-
|
|
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} = ${
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
53
|
+
"@mikro-orm/knex": "7.0.0-dev.40"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
|
-
"@mikro-orm/core": "^6.
|
|
56
|
+
"@mikro-orm/core": "^6.6.0"
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|
|
59
|
-
"@mikro-orm/core": "7.0.0-dev.
|
|
59
|
+
"@mikro-orm/core": "7.0.0-dev.40"
|
|
60
60
|
}
|
|
61
61
|
}
|