@mikro-orm/entity-generator 7.0.0-dev.1 → 7.0.0-dev.100
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/CoreImportsHelper.js +1 -4
- package/DefineEntitySourceFile.d.ts +5 -0
- package/DefineEntitySourceFile.js +115 -0
- package/EntityGenerator.d.ts +1 -1
- package/EntityGenerator.js +57 -46
- package/EntitySchemaSourceFile.d.ts +4 -3
- package/EntitySchemaSourceFile.js +58 -51
- package/NativeEnumSourceFile.d.ts +16 -0
- package/NativeEnumSourceFile.js +47 -0
- package/README.md +3 -2
- package/SourceFile.d.ts +7 -4
- package/SourceFile.js +178 -77
- package/index.d.ts +1 -1
- package/index.js +1 -17
- package/package.json +8 -17
- package/index.mjs +0 -4
package/CoreImportsHelper.js
CHANGED
|
@@ -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.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type GenerateOptions, type MikroORM } from '@mikro-orm/core';
|
|
2
|
-
import { type EntityManager } from '@mikro-orm/
|
|
2
|
+
import { type EntityManager } from '@mikro-orm/sql';
|
|
3
3
|
export declare class EntityGenerator {
|
|
4
4
|
private readonly em;
|
|
5
5
|
private readonly config;
|
package/EntityGenerator.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class EntityGenerator {
|
|
1
|
+
import { EntityMetadata, ReferenceKind, types, Utils, } from '@mikro-orm/core';
|
|
2
|
+
import { DatabaseSchema, } from '@mikro-orm/sql';
|
|
3
|
+
import { fs } from '@mikro-orm/core/fs-utils';
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
5
|
+
import { writeFile } from 'node:fs/promises';
|
|
6
|
+
import { DefineEntitySourceFile } from './DefineEntitySourceFile.js';
|
|
7
|
+
import { EntitySchemaSourceFile } from './EntitySchemaSourceFile.js';
|
|
8
|
+
import { NativeEnumSourceFile } from './NativeEnumSourceFile.js';
|
|
9
|
+
import { SourceFile } from './SourceFile.js';
|
|
10
|
+
export class EntityGenerator {
|
|
11
11
|
em;
|
|
12
12
|
config;
|
|
13
13
|
driver;
|
|
@@ -30,33 +30,45 @@ class EntityGenerator {
|
|
|
30
30
|
orm.config.registerExtension('@mikro-orm/entity-generator', () => new EntityGenerator(orm.em));
|
|
31
31
|
}
|
|
32
32
|
async generate(options = {}) {
|
|
33
|
-
options =
|
|
34
|
-
const schema = await
|
|
33
|
+
options = Utils.mergeConfig({}, this.config.get('entityGenerator'), options);
|
|
34
|
+
const schema = await DatabaseSchema.create(this.connection, this.platform, this.config, undefined, undefined, options.takeTables, options.skipTables);
|
|
35
35
|
const metadata = await this.getEntityMetadata(schema, options);
|
|
36
36
|
const defaultPath = `${this.config.get('baseDir')}/generated-entities`;
|
|
37
|
-
const baseDir =
|
|
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
|
+
}
|
|
38
47
|
for (const meta of metadata) {
|
|
39
|
-
if (
|
|
40
|
-
|
|
41
|
-
this.sources.push(new EntitySchemaSourceFile_1.EntitySchemaSourceFile(meta, this.namingStrategy, this.platform, { ...options, scalarTypeInDecorator: true }));
|
|
42
|
-
}
|
|
43
|
-
else {
|
|
44
|
-
this.sources.push(new SourceFile_1.SourceFile(meta, this.namingStrategy, this.platform, options));
|
|
45
|
-
}
|
|
48
|
+
if (meta.pivotTable && !options.outputPurePivotTables && !this.referencedEntities.has(meta)) {
|
|
49
|
+
continue;
|
|
46
50
|
}
|
|
51
|
+
this.sources.push(new map[options.entityDefinition](meta, this.namingStrategy, this.platform, options));
|
|
47
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()]);
|
|
48
57
|
if (options.save) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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);
|
|
58
70
|
}
|
|
59
|
-
return
|
|
71
|
+
return files.map(([, data]) => data);
|
|
60
72
|
}
|
|
61
73
|
async getEntityMetadata(schema, options) {
|
|
62
74
|
const metadata = schema.getTables()
|
|
@@ -76,11 +88,11 @@ class EntityGenerator {
|
|
|
76
88
|
for (const meta of metadata) {
|
|
77
89
|
for (const prop of meta.relations) {
|
|
78
90
|
if (!metadata.some(otherMeta => prop.referencedTableName === otherMeta.collection || prop.referencedTableName === `${otherMeta.schema ?? schema.name}.${otherMeta.collection}`)) {
|
|
79
|
-
prop.kind =
|
|
91
|
+
prop.kind = ReferenceKind.SCALAR;
|
|
80
92
|
const mappedTypes = prop.columnTypes.map((t, i) => this.platform.getMappedType(t));
|
|
81
93
|
const runtimeTypes = mappedTypes.map(t => t.runtimeType);
|
|
82
94
|
prop.runtimeType = (runtimeTypes.length === 1 ? runtimeTypes[0] : `[${runtimeTypes.join(', ')}]`);
|
|
83
|
-
prop.type = mappedTypes.length === 1 ? (
|
|
95
|
+
prop.type = mappedTypes.length === 1 ? (Utils.entries(types).find(([k, v]) => Object.getPrototypeOf(mappedTypes[0]) === v.prototype)?.[0] ?? mappedTypes[0].name) : 'unknown';
|
|
84
96
|
}
|
|
85
97
|
const meta2 = metadata.find(meta2 => meta2.className === prop.type);
|
|
86
98
|
const targetPrimaryColumns = meta2?.getPrimaryProps().flatMap(p => p.fieldNames);
|
|
@@ -93,7 +105,7 @@ class EntityGenerator {
|
|
|
93
105
|
}
|
|
94
106
|
await options.onInitialMetadata?.(metadata, this.platform);
|
|
95
107
|
// enforce schema usage in class names only on duplicates
|
|
96
|
-
const duplicates =
|
|
108
|
+
const duplicates = Utils.findDuplicates(metadata.map(meta => meta.className));
|
|
97
109
|
for (const duplicate of duplicates) {
|
|
98
110
|
for (const meta of metadata.filter(meta => meta.className === duplicate)) {
|
|
99
111
|
meta.className = this.namingStrategy.getEntityName(`${meta.schema ?? schema.name}_${meta.className}`);
|
|
@@ -131,7 +143,7 @@ class EntityGenerator {
|
|
|
131
143
|
for (const meta of metadata) {
|
|
132
144
|
const isReferenced = metadata.some(m => {
|
|
133
145
|
return m.tableName !== meta.tableName && m.relations.some(r => {
|
|
134
|
-
return r.referencedTableName === meta.tableName && [
|
|
146
|
+
return r.referencedTableName === meta.tableName && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(r.kind);
|
|
135
147
|
});
|
|
136
148
|
});
|
|
137
149
|
if (isReferenced) {
|
|
@@ -144,7 +156,7 @@ class EntityGenerator {
|
|
|
144
156
|
// Entities where there are not exactly 2 PK relations that are both ManyToOne are never pivot tables. Skip.
|
|
145
157
|
const pkRelations = meta.relations.filter(rel => rel.primary);
|
|
146
158
|
if (pkRelations.length !== 2 ||
|
|
147
|
-
pkRelations.some(rel => rel.kind !==
|
|
159
|
+
pkRelations.some(rel => rel.kind !== ReferenceKind.MANY_TO_ONE)) {
|
|
148
160
|
continue;
|
|
149
161
|
}
|
|
150
162
|
const pkRelationFields = new Set(pkRelations.flatMap(rel => rel.fieldNames));
|
|
@@ -196,7 +208,7 @@ class EntityGenerator {
|
|
|
196
208
|
const name = this.namingStrategy.columnNameToProperty(meta.tableName.replace(new RegExp('^' + owner.tableName + '_'), ''));
|
|
197
209
|
const ownerProp = {
|
|
198
210
|
name,
|
|
199
|
-
kind:
|
|
211
|
+
kind: ReferenceKind.MANY_TO_MANY,
|
|
200
212
|
pivotTable: meta.tableName,
|
|
201
213
|
type: meta.relations[1].type,
|
|
202
214
|
joinColumns: meta.relations[0].fieldNames,
|
|
@@ -229,21 +241,21 @@ class EntityGenerator {
|
|
|
229
241
|
type: meta.className,
|
|
230
242
|
joinColumns: prop.fieldNames,
|
|
231
243
|
referencedTableName: meta.tableName,
|
|
232
|
-
referencedColumnNames:
|
|
244
|
+
referencedColumnNames: Utils.flatten(targetMeta.getPrimaryProps().map(pk => pk.fieldNames)),
|
|
233
245
|
mappedBy: prop.name,
|
|
234
246
|
persist: prop.persist,
|
|
235
247
|
};
|
|
236
|
-
if (prop.kind ===
|
|
237
|
-
newProp.kind =
|
|
248
|
+
if (prop.kind === ReferenceKind.MANY_TO_ONE) {
|
|
249
|
+
newProp.kind = ReferenceKind.ONE_TO_MANY;
|
|
238
250
|
}
|
|
239
|
-
else if (prop.kind ===
|
|
240
|
-
newProp.kind =
|
|
251
|
+
else if (prop.kind === ReferenceKind.ONE_TO_ONE && !prop.mappedBy) {
|
|
252
|
+
newProp.kind = ReferenceKind.ONE_TO_ONE;
|
|
241
253
|
newProp.nullable = true;
|
|
242
254
|
newProp.default = null;
|
|
243
255
|
newProp.defaultRaw = 'null';
|
|
244
256
|
}
|
|
245
|
-
else if (prop.kind ===
|
|
246
|
-
newProp.kind =
|
|
257
|
+
else if (prop.kind === ReferenceKind.MANY_TO_MANY && !prop.mappedBy) {
|
|
258
|
+
newProp.kind = ReferenceKind.MANY_TO_MANY;
|
|
247
259
|
}
|
|
248
260
|
else {
|
|
249
261
|
continue;
|
|
@@ -260,7 +272,7 @@ class EntityGenerator {
|
|
|
260
272
|
generateIdentifiedReferences(metadata) {
|
|
261
273
|
for (const meta of metadata.filter(m => !m.pivotTable || this.referencedEntities.has(m))) {
|
|
262
274
|
for (const prop of Object.values(meta.properties)) {
|
|
263
|
-
if ([
|
|
275
|
+
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) || prop.lazy) {
|
|
264
276
|
prop.ref = true;
|
|
265
277
|
}
|
|
266
278
|
}
|
|
@@ -276,7 +288,7 @@ class EntityGenerator {
|
|
|
276
288
|
meta.extends ??= customBaseEntityName;
|
|
277
289
|
}
|
|
278
290
|
if (!baseClassExists) {
|
|
279
|
-
metadata.push(new
|
|
291
|
+
metadata.push(new EntityMetadata({
|
|
280
292
|
className: customBaseEntityName,
|
|
281
293
|
abstract: true,
|
|
282
294
|
relations: [],
|
|
@@ -294,4 +306,3 @@ class EntityGenerator {
|
|
|
294
306
|
}
|
|
295
307
|
}
|
|
296
308
|
}
|
|
297
|
-
exports.EntityGenerator = EntityGenerator;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { type Dictionary, type EntityProperty } from '@mikro-orm/core';
|
|
2
|
-
import { SourceFile } from './SourceFile';
|
|
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
|
}
|
|
@@ -1,25 +1,57 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const core_1 = require("@mikro-orm/core");
|
|
5
|
-
const SourceFile_1 = require("./SourceFile");
|
|
6
|
-
class EntitySchemaSourceFile extends SourceFile_1.SourceFile {
|
|
1
|
+
import { Config, ReferenceKind, } from '@mikro-orm/core';
|
|
2
|
+
import { SourceFile } from './SourceFile.js';
|
|
3
|
+
export class EntitySchemaSourceFile extends SourceFile {
|
|
7
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() {
|
|
8
42
|
let classBody = '';
|
|
9
|
-
if (this.meta.className === this.options.customBaseEntityName) {
|
|
43
|
+
if (!this.options.customBaseEntityName || this.meta.className === this.options.customBaseEntityName) {
|
|
10
44
|
const defineConfigTypeSettings = {};
|
|
11
45
|
defineConfigTypeSettings.forceObject = this.platform.getConfig().get('serialization').forceObject ?? false;
|
|
12
|
-
|
|
46
|
+
if (defineConfigTypeSettings.forceObject) {
|
|
47
|
+
classBody += `${' '.repeat(2)}[${this.referenceCoreImport('Config')}]?: ${this.referenceCoreImport('DefineConfig')}<${this.serializeObject(defineConfigTypeSettings)}>;\n`;
|
|
48
|
+
}
|
|
13
49
|
}
|
|
14
|
-
const enumDefinitions = [];
|
|
15
50
|
const eagerProperties = [];
|
|
16
51
|
const primaryProps = [];
|
|
17
52
|
const props = [];
|
|
18
53
|
for (const prop of Object.values(this.meta.properties)) {
|
|
19
54
|
props.push(this.getPropertyDefinition(prop, 2));
|
|
20
|
-
if (prop.enum && (typeof prop.kind === 'undefined' || prop.kind === core_1.ReferenceKind.SCALAR)) {
|
|
21
|
-
enumDefinitions.push(this.getEnumClassDefinition(prop, 2));
|
|
22
|
-
}
|
|
23
55
|
if (prop.eager) {
|
|
24
56
|
eagerProperties.push(prop);
|
|
25
57
|
}
|
|
@@ -41,50 +73,26 @@ class EntitySchemaSourceFile extends SourceFile_1.SourceFile {
|
|
|
41
73
|
classBody += `${' '.repeat(2)}[${this.referenceCoreImport('EagerProps')}]?: ${eagerPropertyNames.join(' | ')};\n`;
|
|
42
74
|
}
|
|
43
75
|
classBody += `${props.join('')}`;
|
|
44
|
-
|
|
45
|
-
if (enumDefinitions.length) {
|
|
46
|
-
ret += '\n' + enumDefinitions.join('\n');
|
|
47
|
-
}
|
|
48
|
-
ret += `\n`;
|
|
49
|
-
const entitySchemaOptions = {
|
|
50
|
-
class: this.meta.className,
|
|
51
|
-
...(this.meta.embeddable ? this.getEmbeddableDeclOptions() : (this.meta.collection ? this.getEntityDeclOptions() : {})),
|
|
52
|
-
};
|
|
53
|
-
const declLine = `export const ${this.meta.className}Schema = new ${this.referenceCoreImport('EntitySchema')}(`;
|
|
54
|
-
ret += declLine;
|
|
55
|
-
if (this.meta.indexes.length > 0) {
|
|
56
|
-
entitySchemaOptions.indexes = this.meta.indexes.map(index => this.getIndexOptions(index));
|
|
57
|
-
}
|
|
58
|
-
if (this.meta.uniques.length > 0) {
|
|
59
|
-
entitySchemaOptions.uniques = this.meta.uniques.map(index => this.getUniqueOptions(index));
|
|
60
|
-
}
|
|
61
|
-
entitySchemaOptions.properties = Object.fromEntries(Object.entries(this.meta.properties).map(([name, prop]) => [name, this.getPropertyOptions(prop)]));
|
|
62
|
-
// Force top level and properties to be indented, regardless of line length
|
|
63
|
-
entitySchemaOptions[core_1.Config] = true;
|
|
64
|
-
entitySchemaOptions.properties[core_1.Config] = true;
|
|
65
|
-
ret += this.serializeObject(entitySchemaOptions, declLine.length > 80 ? undefined : 80 - declLine.length, 0);
|
|
66
|
-
ret += ');\n';
|
|
67
|
-
ret = `${this.generateImports()}\n\n${ret}`;
|
|
68
|
-
return ret;
|
|
76
|
+
return this.getEntityClass(classBody);
|
|
69
77
|
}
|
|
70
|
-
getPropertyOptions(prop) {
|
|
78
|
+
getPropertyOptions(prop, quote = true) {
|
|
71
79
|
const options = {};
|
|
72
80
|
if (prop.primary) {
|
|
73
81
|
options.primary = true;
|
|
74
82
|
}
|
|
75
|
-
if (typeof prop.kind !== 'undefined' && prop.kind !==
|
|
83
|
+
if (typeof prop.kind !== 'undefined' && prop.kind !== ReferenceKind.SCALAR) {
|
|
76
84
|
options.kind = this.quote(prop.kind);
|
|
77
85
|
}
|
|
78
|
-
if (prop.kind ===
|
|
86
|
+
if (prop.kind === ReferenceKind.MANY_TO_MANY) {
|
|
79
87
|
this.getManyToManyDecoratorOptions(options, prop);
|
|
80
88
|
}
|
|
81
|
-
else if (prop.kind ===
|
|
89
|
+
else if (prop.kind === ReferenceKind.ONE_TO_MANY) {
|
|
82
90
|
this.getOneToManyDecoratorOptions(options, prop);
|
|
83
91
|
}
|
|
84
|
-
else if (prop.kind ===
|
|
85
|
-
this.getScalarPropertyDecoratorOptions(options, prop);
|
|
92
|
+
else if (prop.kind === ReferenceKind.SCALAR || typeof prop.kind === 'undefined') {
|
|
93
|
+
this.getScalarPropertyDecoratorOptions(options, prop, quote);
|
|
86
94
|
}
|
|
87
|
-
else if (prop.kind ===
|
|
95
|
+
else if (prop.kind === ReferenceKind.EMBEDDED) {
|
|
88
96
|
this.getEmbeddedPropertyDeclarationOptions(options, prop);
|
|
89
97
|
}
|
|
90
98
|
else {
|
|
@@ -98,7 +106,7 @@ class EntitySchemaSourceFile extends SourceFile_1.SourceFile {
|
|
|
98
106
|
return options;
|
|
99
107
|
}
|
|
100
108
|
getPropertyIndexesOptions(prop, options) {
|
|
101
|
-
if (prop.kind ===
|
|
109
|
+
if (prop.kind === ReferenceKind.SCALAR) {
|
|
102
110
|
if (prop.index) {
|
|
103
111
|
options.index = this.quote(prop.index);
|
|
104
112
|
}
|
|
@@ -113,11 +121,11 @@ class EntitySchemaSourceFile extends SourceFile_1.SourceFile {
|
|
|
113
121
|
return;
|
|
114
122
|
}
|
|
115
123
|
const defaultName = this.platform.getIndexName(this.meta.collection, prop.fieldNames, type);
|
|
116
|
-
/*
|
|
124
|
+
/* v8 ignore next */
|
|
117
125
|
options[type] = (propType === true || defaultName === propType) ? 'true' : this.quote(propType);
|
|
118
126
|
const expected = {
|
|
119
127
|
index: this.platform.indexForeignKeys(),
|
|
120
|
-
unique: prop.kind ===
|
|
128
|
+
unique: prop.kind === ReferenceKind.ONE_TO_ONE,
|
|
121
129
|
};
|
|
122
130
|
if (expected[type] && options[type] === 'true') {
|
|
123
131
|
delete options[type];
|
|
@@ -126,15 +134,14 @@ class EntitySchemaSourceFile extends SourceFile_1.SourceFile {
|
|
|
126
134
|
processIndex('index');
|
|
127
135
|
processIndex('unique');
|
|
128
136
|
}
|
|
129
|
-
getScalarPropertyDecoratorOptions(options, prop) {
|
|
137
|
+
getScalarPropertyDecoratorOptions(options, prop, quote = true) {
|
|
130
138
|
if (prop.enum) {
|
|
131
139
|
options.enum = true;
|
|
132
140
|
options.items = `() => ${prop.runtimeType}`;
|
|
133
141
|
}
|
|
134
142
|
else {
|
|
135
|
-
options.type = this.quote(prop.type);
|
|
143
|
+
options.type = quote ? this.quote(prop.type) : prop.type;
|
|
136
144
|
}
|
|
137
|
-
super.getScalarPropertyDecoratorOptions(options, prop);
|
|
145
|
+
super.getScalarPropertyDecoratorOptions(options, prop, quote);
|
|
138
146
|
}
|
|
139
147
|
}
|
|
140
|
-
exports.EntitySchemaSourceFile = EntitySchemaSourceFile;
|
|
@@ -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,11 +9,13 @@ 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
|
-
protected getIndexOptions(index: EntityMetadata['indexes'][number], isAtEntityLevel?: boolean): IndexOptions<Dictionary>;
|
|
16
|
-
protected getUniqueOptions(index: EntityMetadata['uniques'][number], isAtEntityLevel?: boolean): UniqueOptions<Dictionary>;
|
|
17
|
+
protected getIndexOptions(index: EntityMetadata['indexes'][number], isAtEntityLevel?: boolean): IndexOptions<Dictionary, string>;
|
|
18
|
+
protected getUniqueOptions(index: EntityMetadata['uniques'][number], isAtEntityLevel?: boolean): UniqueOptions<Dictionary, string>;
|
|
17
19
|
protected generateImports(): string;
|
|
18
20
|
protected getEntityClass(classBody: string): string;
|
|
19
21
|
getBaseName(extension?: string): string;
|
|
@@ -23,18 +25,19 @@ export declare class SourceFile {
|
|
|
23
25
|
protected serializeObject(options: {}, wordwrap?: number, spaces?: number, level?: number): string;
|
|
24
26
|
protected serializeValue(val: unknown, wordwrap?: number, spaces?: number, level?: number): unknown;
|
|
25
27
|
protected getEntityDeclOptions(): EntityOptions<unknown>;
|
|
26
|
-
protected getEmbeddableDeclOptions(): EmbeddableOptions
|
|
28
|
+
protected getEmbeddableDeclOptions(): EmbeddableOptions<unknown>;
|
|
27
29
|
private getCollectionDecl;
|
|
28
30
|
private getPropertyDecorator;
|
|
29
31
|
protected getPropertyIndexes(prop: EntityProperty, options: Dictionary): string[];
|
|
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
|
}
|