@mikro-orm/entity-generator 7.0.0-dev.8 → 7.0.0-dev.80
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 +32 -17
- 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 +3 -2
- package/SourceFile.d.ts +4 -1
- package/SourceFile.js +137 -38
- package/package.json +5 -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
|
+
}
|
package/EntityGenerator.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { EntityMetadata, ReferenceKind, types, Utils, } from '@mikro-orm/core';
|
|
2
2
|
import { DatabaseSchema, } from '@mikro-orm/knex';
|
|
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;
|
|
@@ -32,28 +35,40 @@ export class EntityGenerator {
|
|
|
32
35
|
const metadata = await this.getEntityMetadata(schema, options);
|
|
33
36
|
const defaultPath = `${this.config.get('baseDir')}/generated-entities`;
|
|
34
37
|
const baseDir = Utils.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 (
|
|
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
|
-
}
|
|
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));
|
|
44
52
|
}
|
|
53
|
+
for (const nativeEnum of Object.values(schema.getNativeEnums())) {
|
|
54
|
+
this.sources.push(new NativeEnumSourceFile({}, this.namingStrategy, this.platform, options, nativeEnum));
|
|
55
|
+
}
|
|
56
|
+
const files = this.sources.map(file => [file.getBaseName(), file.generate()]);
|
|
45
57
|
if (options.save) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
|
71
|
+
return files.map(([, data]) => data);
|
|
57
72
|
}
|
|
58
73
|
async getEntityMetadata(schema, options) {
|
|
59
74
|
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
|
@@ -11,7 +11,6 @@ TypeScript ORM for Node.js based on Data Mapper, [Unit of Work](https://mikro-or
|
|
|
11
11
|
[](https://discord.gg/w8bjxFHS7X)
|
|
12
12
|
[](https://www.npmjs.com/package/@mikro-orm/core)
|
|
13
13
|
[](https://coveralls.io/r/mikro-orm/mikro-orm?branch=master)
|
|
14
|
-
[](https://codeclimate.com/github/mikro-orm/mikro-orm/maintainability)
|
|
15
14
|
[](https://github.com/mikro-orm/mikro-orm/actions?workflow=tests)
|
|
16
15
|
|
|
17
16
|
## 🤔 Unit of What?
|
|
@@ -141,7 +140,7 @@ There is also auto-generated [CHANGELOG.md](CHANGELOG.md) file based on commit m
|
|
|
141
140
|
- [Composite and Foreign Keys as Primary Key](https://mikro-orm.io/docs/composite-keys)
|
|
142
141
|
- [Filters](https://mikro-orm.io/docs/filters)
|
|
143
142
|
- [Using `QueryBuilder`](https://mikro-orm.io/docs/query-builder)
|
|
144
|
-
- [
|
|
143
|
+
- [Populating relations](https://mikro-orm.io/docs/populating-relations)
|
|
145
144
|
- [Property Validation](https://mikro-orm.io/docs/property-validation)
|
|
146
145
|
- [Lifecycle Hooks](https://mikro-orm.io/docs/events#hooks)
|
|
147
146
|
- [Vanilla JS Support](https://mikro-orm.io/docs/usage-with-js)
|
|
@@ -382,6 +381,8 @@ See also the list of contributors who [participated](https://github.com/mikro-or
|
|
|
382
381
|
|
|
383
382
|
Please ⭐️ this repository if this project helped you!
|
|
384
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
|
+
|
|
385
386
|
## 📝 License
|
|
386
387
|
|
|
387
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
|
}
|
package/SourceFile.js
CHANGED
|
@@ -6,14 +6,16 @@ import { POSSIBLE_TYPE_IMPORTS } from './CoreImportsHelper.js';
|
|
|
6
6
|
* @see https://github.com/tc39/proposal-regexp-unicode-property-escapes#other-examples
|
|
7
7
|
*/
|
|
8
8
|
export const identifierRegex = /^(?:[$_\p{ID_Start}])(?:[$\u200C\u200D\p{ID_Continue}])*$/u;
|
|
9
|
-
const primitivesAndLibs = [...SCALAR_TYPES, '
|
|
9
|
+
const primitivesAndLibs = [...SCALAR_TYPES, 'unknown', 'object', 'any'];
|
|
10
10
|
export class SourceFile {
|
|
11
11
|
meta;
|
|
12
12
|
namingStrategy;
|
|
13
13
|
platform;
|
|
14
14
|
options;
|
|
15
15
|
coreImports = new Set();
|
|
16
|
+
decoratorImports = new Set();
|
|
16
17
|
entityImports = new Set();
|
|
18
|
+
enumImports = new Map();
|
|
17
19
|
constructor(meta, namingStrategy, platform, options) {
|
|
18
20
|
this.meta = meta;
|
|
19
21
|
this.namingStrategy = namingStrategy;
|
|
@@ -25,24 +27,24 @@ export class SourceFile {
|
|
|
25
27
|
if (this.meta.embeddable || this.meta.collection) {
|
|
26
28
|
if (this.meta.embeddable) {
|
|
27
29
|
const options = this.getEmbeddableDeclOptions();
|
|
28
|
-
ret += `@${this.
|
|
30
|
+
ret += `@${this.referenceDecoratorImport('Embeddable')}(${Utils.hasObjectKeys(options) ? this.serializeObject(options) : ''})\n`;
|
|
29
31
|
}
|
|
30
32
|
else {
|
|
31
33
|
const options = this.getEntityDeclOptions();
|
|
32
|
-
ret += `@${this.
|
|
34
|
+
ret += `@${this.referenceDecoratorImport('Entity')}(${Utils.hasObjectKeys(options) ? this.serializeObject(options) : ''})\n`;
|
|
33
35
|
}
|
|
34
36
|
}
|
|
35
37
|
for (const index of this.meta.indexes) {
|
|
36
38
|
if (index.properties?.length === 1 && typeof this.meta.properties[index.properties[0]] !== 'undefined') {
|
|
37
39
|
continue;
|
|
38
40
|
}
|
|
39
|
-
ret += `@${this.
|
|
41
|
+
ret += `@${this.referenceDecoratorImport('Index')}(${this.serializeObject(this.getIndexOptions(index))})\n`;
|
|
40
42
|
}
|
|
41
43
|
for (const index of this.meta.uniques) {
|
|
42
44
|
if (index.properties?.length === 1 && typeof this.meta.properties[index.properties[0]] !== 'undefined') {
|
|
43
45
|
continue;
|
|
44
46
|
}
|
|
45
|
-
ret += `@${this.
|
|
47
|
+
ret += `@${this.referenceDecoratorImport('Unique')}(${this.serializeObject(this.getUniqueOptions(index))})\n`;
|
|
46
48
|
}
|
|
47
49
|
let classHead = '';
|
|
48
50
|
if (this.meta.className === this.options.customBaseEntityName) {
|
|
@@ -65,7 +67,10 @@ export class SourceFile {
|
|
|
65
67
|
classBody += definition;
|
|
66
68
|
classBody += '\n';
|
|
67
69
|
if (prop.enum) {
|
|
68
|
-
|
|
70
|
+
const def = this.getEnumClassDefinition(prop, 2);
|
|
71
|
+
if (def.length) {
|
|
72
|
+
enumDefinitions.push(def);
|
|
73
|
+
}
|
|
69
74
|
}
|
|
70
75
|
if (prop.eager) {
|
|
71
76
|
eagerProperties.push(prop);
|
|
@@ -99,9 +104,12 @@ export class SourceFile {
|
|
|
99
104
|
if (typeof index.name === 'string') {
|
|
100
105
|
indexOpt.name = this.quote(index.name);
|
|
101
106
|
}
|
|
102
|
-
if (index.expression) {
|
|
107
|
+
if (typeof index.expression === 'string') {
|
|
103
108
|
indexOpt.expression = this.quote(index.expression);
|
|
104
109
|
}
|
|
110
|
+
else if (typeof index.expression === 'function') {
|
|
111
|
+
indexOpt.expression = `${index.expression}`.replace(')=>`', ') => `');
|
|
112
|
+
}
|
|
105
113
|
if (isAtEntityLevel && index.properties) {
|
|
106
114
|
indexOpt.properties = Utils.asArray(index.properties).map(prop => this.quote('' + prop));
|
|
107
115
|
}
|
|
@@ -112,9 +120,12 @@ export class SourceFile {
|
|
|
112
120
|
if (typeof index.name === 'string') {
|
|
113
121
|
uniqueOpt.name = this.quote(index.name);
|
|
114
122
|
}
|
|
115
|
-
if (index.expression) {
|
|
123
|
+
if (typeof index.expression === 'string') {
|
|
116
124
|
uniqueOpt.expression = this.quote(index.expression);
|
|
117
125
|
}
|
|
126
|
+
else if (typeof index.expression === 'function') {
|
|
127
|
+
uniqueOpt.expression = `${index.expression}`.replace(')=>`', ') => `');
|
|
128
|
+
}
|
|
118
129
|
if (isAtEntityLevel && index.properties) {
|
|
119
130
|
uniqueOpt.properties = Utils.asArray(index.properties).map(prop => this.quote('' + prop));
|
|
120
131
|
}
|
|
@@ -135,37 +146,61 @@ export class SourceFile {
|
|
|
135
146
|
return ret;
|
|
136
147
|
}).join(', '))} } from '@mikro-orm/core';`);
|
|
137
148
|
}
|
|
149
|
+
if (this.decoratorImports.size > 0) {
|
|
150
|
+
const type = this.options.decorators;
|
|
151
|
+
imports.add(`import { ${([...this.decoratorImports].sort().map(t => {
|
|
152
|
+
let ret = t;
|
|
153
|
+
if (this.options.coreImportsPrefix) {
|
|
154
|
+
const resolvedIdentifier = `${this.options.coreImportsPrefix}${t}`;
|
|
155
|
+
ret += ` as ${resolvedIdentifier}`;
|
|
156
|
+
}
|
|
157
|
+
return ret;
|
|
158
|
+
}).join(', '))} } from '@mikro-orm/decorators/${type}';`);
|
|
159
|
+
}
|
|
138
160
|
const extension = this.options.esmImport ? '.js' : '';
|
|
139
161
|
const { dir, base } = parse(`${this.options.path ?? '.'}/${this.getBaseName()}`);
|
|
140
162
|
const basePath = relative(dir, this.options.path ?? '.') || '.';
|
|
141
163
|
(this.options.extraImports?.(basePath, base) ?? []).forEach(v => this.entityImports.add(v));
|
|
142
164
|
const entityImports = [...this.entityImports].filter(e => e !== this.meta.className);
|
|
143
|
-
|
|
165
|
+
const importMap = new Map();
|
|
166
|
+
for (const entity of entityImports) {
|
|
144
167
|
const file = this.options.onImport?.(entity, basePath, extension, base) ?? {
|
|
145
168
|
path: `${basePath}/${this.options.fileName(entity)}${extension}`,
|
|
146
169
|
name: entity,
|
|
147
170
|
};
|
|
148
171
|
if (file.path === '') {
|
|
149
172
|
if (file.name === '') {
|
|
150
|
-
|
|
173
|
+
continue;
|
|
151
174
|
}
|
|
152
|
-
|
|
153
|
-
|
|
175
|
+
importMap.set(file.path, `import ${this.quote(file.name)};`);
|
|
176
|
+
continue;
|
|
154
177
|
}
|
|
155
178
|
if (file.name === '') {
|
|
156
|
-
|
|
157
|
-
|
|
179
|
+
importMap.set(file.path, `import * as ${entity} from ${this.quote(file.path)};`);
|
|
180
|
+
continue;
|
|
158
181
|
}
|
|
159
182
|
if (file.name === 'default') {
|
|
160
|
-
|
|
161
|
-
|
|
183
|
+
importMap.set(file.path, `import ${entity} from ${this.quote(file.path)};`);
|
|
184
|
+
continue;
|
|
162
185
|
}
|
|
163
186
|
if (file.name === entity) {
|
|
164
|
-
|
|
165
|
-
|
|
187
|
+
importMap.set(file.path, `import { ${entity} } from ${this.quote(file.path)};`);
|
|
188
|
+
continue;
|
|
166
189
|
}
|
|
167
|
-
|
|
168
|
-
}
|
|
190
|
+
importMap.set(file.path, `import { ${identifierRegex.test(file.name) ? file.name : this.quote(file.name)} as ${entity} } from ${this.quote(file.path)};`);
|
|
191
|
+
}
|
|
192
|
+
if (this.enumImports.size) {
|
|
193
|
+
for (const [name, exports] of this.enumImports.entries()) {
|
|
194
|
+
const file = this.options.onImport?.(name, basePath, extension, base) ?? {
|
|
195
|
+
path: `${basePath}/${this.options.fileName(name)}${extension}`,
|
|
196
|
+
name,
|
|
197
|
+
};
|
|
198
|
+
importMap.set(file.path, `import { ${exports.join(', ')} } from ${this.quote(file.path)};`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
for (const key of [...importMap.keys()].sort()) {
|
|
202
|
+
imports.add(importMap.get(key));
|
|
203
|
+
}
|
|
169
204
|
return Array.from(imports.values()).join('\n');
|
|
170
205
|
}
|
|
171
206
|
getEntityClass(classBody) {
|
|
@@ -195,6 +230,7 @@ export class SourceFile {
|
|
|
195
230
|
getPropertyDefinition(prop, padLeft) {
|
|
196
231
|
const padding = ' '.repeat(padLeft);
|
|
197
232
|
const propName = identifierRegex.test(prop.name) ? prop.name : this.quote(prop.name);
|
|
233
|
+
const enumMode = this.options.enumMode;
|
|
198
234
|
let hiddenType = '';
|
|
199
235
|
if (prop.hidden) {
|
|
200
236
|
hiddenType += ` & ${this.referenceCoreImport('Hidden')}`;
|
|
@@ -212,7 +248,11 @@ export class SourceFile {
|
|
|
212
248
|
: (() => {
|
|
213
249
|
if (isScalar) {
|
|
214
250
|
if (prop.enum) {
|
|
215
|
-
|
|
251
|
+
const method = enumMode === 'ts-enum' ? 'getEnumClassName' : 'getEnumTypeName';
|
|
252
|
+
if (prop.nativeEnumName) {
|
|
253
|
+
return this.namingStrategy[method](prop.nativeEnumName, undefined, this.meta.schema);
|
|
254
|
+
}
|
|
255
|
+
return this.namingStrategy[method](prop.fieldNames[0], this.meta.collection, this.meta.schema);
|
|
216
256
|
}
|
|
217
257
|
breakdownOfIType = this.breakdownOfIType(prop);
|
|
218
258
|
if (typeof breakdownOfIType !== 'undefined') {
|
|
@@ -247,8 +287,14 @@ export class SourceFile {
|
|
|
247
287
|
return `${padding}${ret};\n`;
|
|
248
288
|
}
|
|
249
289
|
if (prop.enum && typeof prop.default === 'string') {
|
|
290
|
+
if (enumMode === 'union-type') {
|
|
291
|
+
return `${padding}${ret} = ${this.quote(prop.default)};\n`;
|
|
292
|
+
}
|
|
293
|
+
const enumClassName = prop.nativeEnumName
|
|
294
|
+
? this.namingStrategy.getEnumClassName(prop.nativeEnumName, undefined, this.meta.schema)
|
|
295
|
+
: this.namingStrategy.getEnumClassName(prop.fieldNames[0], this.meta.collection, this.meta.schema);
|
|
250
296
|
const enumVal = this.namingStrategy.enumValueToEnumProperty(prop.default, prop.fieldNames[0], this.meta.collection, this.meta.schema);
|
|
251
|
-
return `${padding}${ret} = ${
|
|
297
|
+
return `${padding}${ret} = ${enumClassName}${identifierRegex.test(enumVal) ? `.${enumVal}` : `[${this.quote(enumVal)}]`};\n`;
|
|
252
298
|
}
|
|
253
299
|
if (prop.fieldNames?.length > 1) {
|
|
254
300
|
// TODO: Composite FKs with default values require additions to default/defaultRaw that are not yet supported.
|
|
@@ -264,15 +310,51 @@ export class SourceFile {
|
|
|
264
310
|
return `${padding}${ret} = ${prop.ref ? this.referenceCoreImport('ref') : this.referenceCoreImport('rel')}(${propType}, ${defaultVal});\n`;
|
|
265
311
|
}
|
|
266
312
|
getEnumClassDefinition(prop, padLeft) {
|
|
313
|
+
const enumMode = this.options.enumMode;
|
|
314
|
+
if (prop.nativeEnumName) {
|
|
315
|
+
const imports = [];
|
|
316
|
+
if (enumMode !== 'union-type') {
|
|
317
|
+
imports.push(prop.runtimeType);
|
|
318
|
+
}
|
|
319
|
+
if (!this.options.inferEntityType && enumMode !== 'ts-enum') {
|
|
320
|
+
const enumTypeName = this.namingStrategy.getEnumTypeName(prop.nativeEnumName, undefined, this.meta.schema);
|
|
321
|
+
imports.push(enumTypeName);
|
|
322
|
+
}
|
|
323
|
+
this.enumImports.set(prop.runtimeType, imports);
|
|
324
|
+
return '';
|
|
325
|
+
}
|
|
267
326
|
const enumClassName = this.namingStrategy.getEnumClassName(prop.fieldNames[0], this.meta.collection, this.meta.schema);
|
|
327
|
+
const enumTypeName = this.namingStrategy.getEnumTypeName(prop.fieldNames[0], this.meta.collection, this.meta.schema);
|
|
268
328
|
const padding = ' '.repeat(padLeft);
|
|
269
|
-
let ret = `export enum ${enumClassName} {\n`;
|
|
270
329
|
const enumValues = prop.items;
|
|
330
|
+
if (enumMode === 'union-type') {
|
|
331
|
+
return `export type ${enumTypeName} = ${enumValues.map(item => this.quote(item)).join(' | ')};\n`;
|
|
332
|
+
}
|
|
333
|
+
let ret = '';
|
|
334
|
+
if (enumMode === 'dictionary') {
|
|
335
|
+
ret += `export const ${enumClassName} = {\n`;
|
|
336
|
+
}
|
|
337
|
+
else {
|
|
338
|
+
ret += `export enum ${enumClassName} {\n`;
|
|
339
|
+
}
|
|
271
340
|
for (const enumValue of enumValues) {
|
|
272
341
|
const enumName = this.namingStrategy.enumValueToEnumProperty(enumValue, prop.fieldNames[0], this.meta.collection, this.meta.schema);
|
|
273
|
-
|
|
342
|
+
if (enumMode === 'dictionary') {
|
|
343
|
+
ret += `${padding}${identifierRegex.test(enumName) ? enumName : this.quote(enumName)}: ${this.quote(enumValue)},\n`;
|
|
344
|
+
}
|
|
345
|
+
else {
|
|
346
|
+
ret += `${padding}${identifierRegex.test(enumName) ? enumName : this.quote(enumName)} = ${this.quote(enumValue)},\n`;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
if (enumMode === 'dictionary') {
|
|
350
|
+
ret += '} as const;\n';
|
|
351
|
+
}
|
|
352
|
+
else {
|
|
353
|
+
ret += '}\n';
|
|
354
|
+
}
|
|
355
|
+
if (enumMode === 'dictionary') {
|
|
356
|
+
ret += `\nexport type ${enumTypeName} = (typeof ${enumClassName})[keyof typeof ${enumClassName}];\n`;
|
|
274
357
|
}
|
|
275
|
-
ret += '}\n';
|
|
276
358
|
return ret;
|
|
277
359
|
}
|
|
278
360
|
serializeObject(options, wordwrap, spaces, level = 0) {
|
|
@@ -312,7 +394,7 @@ export class SourceFile {
|
|
|
312
394
|
options.expression = this.quote(this.meta.expression);
|
|
313
395
|
}
|
|
314
396
|
else if (typeof this.meta.expression === 'function') {
|
|
315
|
-
options.expression =
|
|
397
|
+
options.expression = this.meta.expression.toString();
|
|
316
398
|
}
|
|
317
399
|
if (this.meta.repositoryClass) {
|
|
318
400
|
this.entityImports.add(this.meta.repositoryClass);
|
|
@@ -352,7 +434,7 @@ export class SourceFile {
|
|
|
352
434
|
getPropertyDecorator(prop, padLeft) {
|
|
353
435
|
const padding = ' '.repeat(padLeft);
|
|
354
436
|
const options = {};
|
|
355
|
-
let decorator = `@${this.
|
|
437
|
+
let decorator = `@${this.referenceDecoratorImport(this.getDecoratorType(prop))}`;
|
|
356
438
|
if (prop.kind === ReferenceKind.MANY_TO_MANY) {
|
|
357
439
|
this.getManyToManyDecoratorOptions(options, prop);
|
|
358
440
|
}
|
|
@@ -373,7 +455,7 @@ export class SourceFile {
|
|
|
373
455
|
decorator = [...indexes.sort(), decorator].map(d => padding + d).join('\n');
|
|
374
456
|
const decoratorArgs = [];
|
|
375
457
|
if (prop.formula) {
|
|
376
|
-
decoratorArgs.push(
|
|
458
|
+
decoratorArgs.push(prop.formula.toString());
|
|
377
459
|
}
|
|
378
460
|
if (Utils.hasObjectKeys(options)) {
|
|
379
461
|
decoratorArgs.push(`${this.serializeObject(options)}`);
|
|
@@ -402,26 +484,26 @@ export class SourceFile {
|
|
|
402
484
|
let propIndexIsNonTrivialIndex = false;
|
|
403
485
|
const nonTrivialIndexes = this.meta.indexes.filter(i => i.properties?.length === 1 && i.properties[0] === prop.name);
|
|
404
486
|
for (const i of nonTrivialIndexes) {
|
|
405
|
-
ret.push(`@${this.
|
|
487
|
+
ret.push(`@${this.referenceDecoratorImport('Index')}(${this.serializeObject(this.getIndexOptions(i, false))})`);
|
|
406
488
|
if (prop.index === i.name) {
|
|
407
489
|
propIndexIsNonTrivialIndex = true;
|
|
408
490
|
delete options.index;
|
|
409
491
|
}
|
|
410
492
|
}
|
|
411
493
|
if (prop.index && !options.index && !propIndexIsNonTrivialIndex) {
|
|
412
|
-
ret.push(`@${this.
|
|
494
|
+
ret.push(`@${this.referenceDecoratorImport('Index')}(${typeof prop.index === 'string' ? `{ name: ${this.quote(prop.index)} }` : ''})`);
|
|
413
495
|
}
|
|
414
496
|
let propIndexIsNonTrivialUnique = false;
|
|
415
497
|
const nonTrivialUnique = this.meta.uniques.filter(i => i.properties?.length === 1 && i.properties[0] === prop.name);
|
|
416
498
|
for (const i of nonTrivialUnique) {
|
|
417
|
-
ret.push(`@${this.
|
|
499
|
+
ret.push(`@${this.referenceDecoratorImport('Unique')}(${this.serializeObject(this.getUniqueOptions(i, false))})`);
|
|
418
500
|
if (prop.unique === i.name) {
|
|
419
501
|
propIndexIsNonTrivialUnique = true;
|
|
420
502
|
delete options.unique;
|
|
421
503
|
}
|
|
422
504
|
}
|
|
423
505
|
if (prop.unique && !options.unique && !propIndexIsNonTrivialUnique) {
|
|
424
|
-
ret.push(`@${this.
|
|
506
|
+
ret.push(`@${this.referenceDecoratorImport('Unique')}(${typeof prop.unique === 'string' ? `{ name: ${this.quote(prop.unique)} }` : ''})`);
|
|
425
507
|
}
|
|
426
508
|
return ret;
|
|
427
509
|
}
|
|
@@ -432,7 +514,7 @@ export class SourceFile {
|
|
|
432
514
|
if (prop.primary && (prop.enum || !(typeof prop.kind === 'undefined' || prop.kind === ReferenceKind.SCALAR))) {
|
|
433
515
|
options.primary = true;
|
|
434
516
|
}
|
|
435
|
-
['persist', 'hydrate'
|
|
517
|
+
['persist', 'hydrate']
|
|
436
518
|
.filter(key => prop[key] === false)
|
|
437
519
|
.forEach(key => options[key] = false);
|
|
438
520
|
['onCreate', 'onUpdate', 'serializer']
|
|
@@ -531,12 +613,23 @@ export class SourceFile {
|
|
|
531
613
|
this.propTypeBreakdowns.set(prop, r);
|
|
532
614
|
return r;
|
|
533
615
|
}
|
|
534
|
-
getScalarPropertyDecoratorOptions(options, prop) {
|
|
616
|
+
getScalarPropertyDecoratorOptions(options, prop, quote = true) {
|
|
535
617
|
if (prop.fieldNames[0] !== this.namingStrategy.propertyToColumnName(prop.name)) {
|
|
536
618
|
options.fieldName = this.quote(prop.fieldNames[0]);
|
|
537
619
|
}
|
|
538
620
|
if (prop.enum) {
|
|
539
|
-
options.
|
|
621
|
+
if (this.options.enumMode === 'union-type') {
|
|
622
|
+
options.items = `[${prop.items.map(item => this.quote(item)).join(', ')}]`;
|
|
623
|
+
}
|
|
624
|
+
else if (prop.nativeEnumName) {
|
|
625
|
+
const enumClassName = this.namingStrategy.getEnumClassName(prop.nativeEnumName, undefined, this.meta.schema);
|
|
626
|
+
options.items = `() => ${enumClassName}`;
|
|
627
|
+
options.nativeEnumName = this.quote(prop.nativeEnumName);
|
|
628
|
+
}
|
|
629
|
+
else {
|
|
630
|
+
const enumClassName = this.namingStrategy.getEnumClassName(prop.fieldNames[0], this.meta.collection, this.meta.schema);
|
|
631
|
+
options.items = `() => ${enumClassName}`;
|
|
632
|
+
}
|
|
540
633
|
}
|
|
541
634
|
// For enum properties, we don't need a column type
|
|
542
635
|
// or the property length or other information in the decorator.
|
|
@@ -565,7 +658,7 @@ export class SourceFile {
|
|
|
565
658
|
return ((useDefault && !hasUsableNullDefault) || (prop.optional && !prop.nullable));
|
|
566
659
|
})() // also when there is the "| Opt" type modifier (because reflect-metadata can't extract the base)
|
|
567
660
|
) {
|
|
568
|
-
options.type = this.quote(prop.type);
|
|
661
|
+
options.type = quote ? this.quote(prop.type) : prop.type;
|
|
569
662
|
}
|
|
570
663
|
}
|
|
571
664
|
const columnTypeFromMappedRuntimeType = mappedRuntimeType.getColumnType({ ...prop, autoincrement: false }, this.platform);
|
|
@@ -592,7 +685,7 @@ export class SourceFile {
|
|
|
592
685
|
assign('length');
|
|
593
686
|
}
|
|
594
687
|
// those are already included in the `columnType` in most cases, and when that option is present, they would be ignored anyway
|
|
595
|
-
/* v8 ignore next
|
|
688
|
+
/* v8 ignore next */
|
|
596
689
|
if (mappedColumnType instanceof DecimalType && !options.columnType) {
|
|
597
690
|
assign('precision');
|
|
598
691
|
assign('scale');
|
|
@@ -666,7 +759,7 @@ export class SourceFile {
|
|
|
666
759
|
if (prop.array) {
|
|
667
760
|
options.array = true;
|
|
668
761
|
}
|
|
669
|
-
if (prop.object) {
|
|
762
|
+
if (prop.object && !prop.array) {
|
|
670
763
|
options.object = true;
|
|
671
764
|
}
|
|
672
765
|
if (prop.prefix === false || typeof prop.prefix === 'string') {
|
|
@@ -749,4 +842,10 @@ export class SourceFile {
|
|
|
749
842
|
? `${this.options.coreImportsPrefix}${identifier}`
|
|
750
843
|
: identifier;
|
|
751
844
|
}
|
|
845
|
+
referenceDecoratorImport(identifier) {
|
|
846
|
+
this.decoratorImports.add(identifier);
|
|
847
|
+
return this.options.coreImportsPrefix
|
|
848
|
+
? `${this.options.coreImportsPrefix}${identifier}`
|
|
849
|
+
: identifier;
|
|
850
|
+
}
|
|
752
851
|
}
|
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.80",
|
|
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",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
},
|
|
39
39
|
"homepage": "https://mikro-orm.io",
|
|
40
40
|
"engines": {
|
|
41
|
-
"node": ">= 22.
|
|
41
|
+
"node": ">= 22.17.0"
|
|
42
42
|
},
|
|
43
43
|
"scripts": {
|
|
44
44
|
"build": "yarn clean && yarn compile && yarn copy",
|
|
@@ -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.80"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
|
-
"@mikro-orm/core": "^6.
|
|
56
|
+
"@mikro-orm/core": "^6.6.1"
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|
|
59
|
-
"@mikro-orm/core": "7.0.0-dev.
|
|
59
|
+
"@mikro-orm/core": "7.0.0-dev.80"
|
|
60
60
|
}
|
|
61
61
|
}
|