@mikro-orm/reflection 7.0.4 → 7.0.5-dev.1
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/README.md +1 -1
- package/TsMorphMetadataProvider.d.ts +18 -18
- package/TsMorphMetadataProvider.js +233 -248
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -133,7 +133,7 @@ const author = await em.findOneOrFail(Author, 1, {
|
|
|
133
133
|
populate: ['books'],
|
|
134
134
|
});
|
|
135
135
|
author.name = 'Jon Snow II';
|
|
136
|
-
author.books.getItems().forEach(book =>
|
|
136
|
+
author.books.getItems().forEach(book => book.title += ' (2nd ed.)');
|
|
137
137
|
author.books.add(orm.em.create(Book, { title: 'New Book', author }));
|
|
138
138
|
|
|
139
139
|
// Flush computes change sets and executes them in a single transaction
|
|
@@ -2,22 +2,22 @@ import { type SourceFile } from 'ts-morph';
|
|
|
2
2
|
import { type EntityMetadata, MetadataProvider } from '@mikro-orm/core';
|
|
3
3
|
/** Metadata provider that uses ts-morph to infer property types from TypeScript source files or declaration files. */
|
|
4
4
|
export declare class TsMorphMetadataProvider extends MetadataProvider {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
5
|
+
private project;
|
|
6
|
+
private sources;
|
|
7
|
+
static useCache(): boolean;
|
|
8
|
+
useCache(): boolean;
|
|
9
|
+
loadEntityMetadata(meta: EntityMetadata): void;
|
|
10
|
+
getExistingSourceFile(path: string, ext?: string, validate?: boolean): SourceFile;
|
|
11
|
+
protected initProperties(meta: EntityMetadata): void;
|
|
12
|
+
private extractType;
|
|
13
|
+
private cleanUpTypeTags;
|
|
14
|
+
private initPropertyType;
|
|
15
|
+
private readTypeFromSource;
|
|
16
|
+
private getSourceFile;
|
|
17
|
+
private stripRelativePath;
|
|
18
|
+
private processWrapper;
|
|
19
|
+
private initProject;
|
|
20
|
+
private initSourceFiles;
|
|
21
|
+
saveToCache(meta: EntityMetadata): void;
|
|
22
|
+
getCacheKey(meta: Pick<EntityMetadata, 'className' | 'path'>): string;
|
|
23
23
|
}
|
|
@@ -1,271 +1,256 @@
|
|
|
1
1
|
import { extname } from 'node:path';
|
|
2
|
-
import { ComputedPropertyName, ModuleKind, NoSubstitutionTemplateLiteral, Project, StringLiteral } from 'ts-morph';
|
|
3
|
-
import {
|
|
4
|
-
EntitySchema,
|
|
5
|
-
MetadataError,
|
|
6
|
-
MetadataProvider,
|
|
7
|
-
MetadataStorage,
|
|
8
|
-
RawQueryFragment,
|
|
9
|
-
ReferenceKind,
|
|
10
|
-
Type,
|
|
11
|
-
Utils,
|
|
12
|
-
} from '@mikro-orm/core';
|
|
2
|
+
import { ComputedPropertyName, ModuleKind, NoSubstitutionTemplateLiteral, Project, StringLiteral, } from 'ts-morph';
|
|
3
|
+
import { EntitySchema, MetadataError, MetadataProvider, MetadataStorage, RawQueryFragment, ReferenceKind, Type, Utils, } from '@mikro-orm/core';
|
|
13
4
|
import { fs } from '@mikro-orm/core/fs-utils';
|
|
14
5
|
/** Metadata provider that uses ts-morph to infer property types from TypeScript source files or declaration files. */
|
|
15
6
|
export class TsMorphMetadataProvider extends MetadataProvider {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
useCache() {
|
|
22
|
-
return this.config.get('metadataCache').enabled ?? TsMorphMetadataProvider.useCache();
|
|
23
|
-
}
|
|
24
|
-
loadEntityMetadata(meta) {
|
|
25
|
-
if (!meta.path) {
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
this.initProperties(meta);
|
|
29
|
-
}
|
|
30
|
-
getExistingSourceFile(path, ext, validate = true) {
|
|
31
|
-
if (!ext) {
|
|
32
|
-
return this.getExistingSourceFile(path, '.d.ts', false) || this.getExistingSourceFile(path, '.ts');
|
|
33
|
-
}
|
|
34
|
-
const tsPath = /.*\/[^/]+$/.exec(path)[0].replace(/\.js$/, ext);
|
|
35
|
-
return this.getSourceFile(tsPath, validate);
|
|
36
|
-
}
|
|
37
|
-
initProperties(meta) {
|
|
38
|
-
meta.path = fs.normalizePath(meta.path);
|
|
39
|
-
// load types and column names
|
|
40
|
-
for (const prop of Object.values(meta.properties)) {
|
|
41
|
-
const { type, target } = this.extractType(meta, prop);
|
|
42
|
-
this.initPropertyType(meta, prop);
|
|
43
|
-
prop.type = type ?? prop.type;
|
|
44
|
-
prop.target = target;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
extractType(meta, prop) {
|
|
48
|
-
/* v8 ignore next */
|
|
49
|
-
if (typeof prop.entity === 'string') {
|
|
50
|
-
throw new Error(
|
|
51
|
-
`Relation target needs to be an entity class or EntitySchema instance, '${prop.entity}' given instead for ${meta.className}.${prop.name}.`,
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
if (!prop.entity) {
|
|
55
|
-
return { type: prop.type };
|
|
56
|
-
}
|
|
57
|
-
const tmp = prop.entity();
|
|
58
|
-
const target = EntitySchema.is(tmp) ? tmp.meta.class : tmp;
|
|
59
|
-
return { type: Utils.className(target), target };
|
|
60
|
-
}
|
|
61
|
-
cleanUpTypeTags(type) {
|
|
62
|
-
const genericTags = [/Opt<(.*?)>/, /Hidden<(.*?)>/, /RequiredNullable<(.*?)>/];
|
|
63
|
-
const intersectionTags = ['Opt.Brand', 'Hidden.Brand', 'RequiredNullable.Brand'];
|
|
64
|
-
for (const tag of genericTags) {
|
|
65
|
-
type = type.replace(tag, '$1');
|
|
66
|
-
}
|
|
67
|
-
for (const tag of intersectionTags) {
|
|
68
|
-
type = type.replace(' & ' + tag, '');
|
|
69
|
-
type = type.replace(tag + ' & ', '');
|
|
70
|
-
}
|
|
71
|
-
return type;
|
|
72
|
-
}
|
|
73
|
-
initPropertyType(meta, prop) {
|
|
74
|
-
const { type: typeRaw, optional } = this.readTypeFromSource(meta, prop);
|
|
75
|
-
prop.type = this.cleanUpTypeTags(typeRaw);
|
|
76
|
-
if (optional) {
|
|
77
|
-
prop.optional = true;
|
|
78
|
-
}
|
|
79
|
-
this.processWrapper(prop, 'Ref');
|
|
80
|
-
this.processWrapper(prop, 'Reference');
|
|
81
|
-
this.processWrapper(prop, 'EntityRef');
|
|
82
|
-
this.processWrapper(prop, 'ScalarRef');
|
|
83
|
-
this.processWrapper(prop, 'ScalarReference');
|
|
84
|
-
this.processWrapper(prop, 'Collection');
|
|
85
|
-
prop.runtimeType ??= prop.type;
|
|
86
|
-
if (/^(Dictionary|Record)<.*>$/.exec(prop.type.replace(/import\(.*\)\./g, ''))) {
|
|
87
|
-
prop.type = 'json';
|
|
7
|
+
project;
|
|
8
|
+
sources;
|
|
9
|
+
static useCache() {
|
|
10
|
+
return true;
|
|
88
11
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
const source = this.getExistingSourceFile(meta.path);
|
|
92
|
-
const cls = source.getClass(meta.className);
|
|
93
|
-
/* v8 ignore next */
|
|
94
|
-
if (!cls) {
|
|
95
|
-
throw new MetadataError(
|
|
96
|
-
`Source class for entity ${meta.className} not found. Verify you have 'compilerOptions.declaration' enabled in your 'tsconfig.json'. If you are using webpack, see https://bit.ly/35pPDNn`,
|
|
97
|
-
);
|
|
12
|
+
useCache() {
|
|
13
|
+
return this.config.get('metadataCache').enabled ?? TsMorphMetadataProvider.useCache();
|
|
98
14
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
return true;
|
|
103
|
-
}
|
|
104
|
-
const nameNode = v.getNameNode();
|
|
105
|
-
if (nameNode instanceof StringLiteral && nameNode.getLiteralText() === prop.name) {
|
|
106
|
-
return true;
|
|
107
|
-
}
|
|
108
|
-
if (nameNode instanceof ComputedPropertyName) {
|
|
109
|
-
const expr = nameNode.getExpression();
|
|
110
|
-
if (expr instanceof NoSubstitutionTemplateLiteral && expr.getLiteralText() === prop.name) {
|
|
111
|
-
return true;
|
|
15
|
+
loadEntityMetadata(meta) {
|
|
16
|
+
if (!meta.path) {
|
|
17
|
+
return;
|
|
112
18
|
}
|
|
113
|
-
|
|
114
|
-
return false;
|
|
115
|
-
});
|
|
116
|
-
if (!property) {
|
|
117
|
-
return { type: prop.type, optional: prop.nullable };
|
|
118
|
-
}
|
|
119
|
-
const tsType = property.getType();
|
|
120
|
-
const typeName = tsType.getText(property);
|
|
121
|
-
if (prop.enum && tsType.isEnum()) {
|
|
122
|
-
prop.items = tsType.getUnionTypes().map(t => t.getLiteralValueOrThrow());
|
|
19
|
+
this.initProperties(meta);
|
|
123
20
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
.getUnionTypes()
|
|
131
|
-
.map(t => t.getLiteralValueOrThrow());
|
|
132
|
-
}
|
|
21
|
+
getExistingSourceFile(path, ext, validate = true) {
|
|
22
|
+
if (!ext) {
|
|
23
|
+
return this.getExistingSourceFile(path, '.d.ts', false) || this.getExistingSourceFile(path, '.ts');
|
|
24
|
+
}
|
|
25
|
+
const tsPath = /.*\/[^/]+$/.exec(path)[0].replace(/\.js$/, ext);
|
|
26
|
+
return this.getSourceFile(tsPath, validate);
|
|
133
27
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
28
|
+
initProperties(meta) {
|
|
29
|
+
meta.path = fs.normalizePath(meta.path);
|
|
30
|
+
// load types and column names
|
|
31
|
+
for (const prop of Object.values(meta.properties)) {
|
|
32
|
+
const { type, target } = this.extractType(meta, prop);
|
|
33
|
+
this.initPropertyType(meta, prop);
|
|
34
|
+
prop.type = type ?? prop.type;
|
|
35
|
+
prop.target = target;
|
|
36
|
+
}
|
|
142
37
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
38
|
+
extractType(meta, prop) {
|
|
39
|
+
/* v8 ignore next */
|
|
40
|
+
if (typeof prop.entity === 'string') {
|
|
41
|
+
throw new Error(`Relation target needs to be an entity class or EntitySchema instance, '${prop.entity}' given instead for ${meta.className}.${prop.name}.`);
|
|
42
|
+
}
|
|
43
|
+
if (!prop.entity) {
|
|
44
|
+
return { type: prop.type };
|
|
45
|
+
}
|
|
46
|
+
const tmp = prop.entity();
|
|
47
|
+
const target = EntitySchema.is(tmp) ? tmp.meta.class : tmp;
|
|
48
|
+
return { type: Utils.className(target), target };
|
|
150
49
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
50
|
+
cleanUpTypeTags(type) {
|
|
51
|
+
const genericTags = [/Opt<(.*?)>/, /Hidden<(.*?)>/, /RequiredNullable<(.*?)>/];
|
|
52
|
+
const intersectionTags = ['Opt.Brand', 'Hidden.Brand', 'RequiredNullable.Brand'];
|
|
53
|
+
for (const tag of genericTags) {
|
|
54
|
+
type = type.replace(tag, '$1');
|
|
55
|
+
}
|
|
56
|
+
for (const tag of intersectionTags) {
|
|
57
|
+
type = type.replace(' & ' + tag, '');
|
|
58
|
+
type = type.replace(tag + ' & ', '');
|
|
59
|
+
}
|
|
60
|
+
return type;
|
|
156
61
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
62
|
+
initPropertyType(meta, prop) {
|
|
63
|
+
const { type: typeRaw, optional } = this.readTypeFromSource(meta, prop);
|
|
64
|
+
prop.type = this.cleanUpTypeTags(typeRaw);
|
|
65
|
+
if (optional) {
|
|
66
|
+
prop.optional = true;
|
|
67
|
+
}
|
|
68
|
+
this.processWrapper(prop, 'Ref');
|
|
69
|
+
this.processWrapper(prop, 'Reference');
|
|
70
|
+
this.processWrapper(prop, 'EntityRef');
|
|
71
|
+
this.processWrapper(prop, 'ScalarRef');
|
|
72
|
+
this.processWrapper(prop, 'ScalarReference');
|
|
73
|
+
this.processWrapper(prop, 'Collection');
|
|
74
|
+
prop.runtimeType ??= prop.type;
|
|
75
|
+
if (/^(Dictionary|Record)<.*>$/.exec(prop.type.replace(/import\(.*\)\./g, ''))) {
|
|
76
|
+
prop.type = 'json';
|
|
77
|
+
}
|
|
164
78
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
79
|
+
readTypeFromSource(meta, prop) {
|
|
80
|
+
const source = this.getExistingSourceFile(meta.path);
|
|
81
|
+
const cls = source.getClass(meta.className);
|
|
82
|
+
/* v8 ignore next */
|
|
83
|
+
if (!cls) {
|
|
84
|
+
throw new MetadataError(`Source class for entity ${meta.className} not found. Verify you have 'compilerOptions.declaration' enabled in your 'tsconfig.json'. If you are using webpack, see https://bit.ly/35pPDNn`);
|
|
85
|
+
}
|
|
86
|
+
const properties = cls.getInstanceProperties();
|
|
87
|
+
const property = properties.find(v => {
|
|
88
|
+
if (v.getName() === prop.name) {
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
const nameNode = v.getNameNode();
|
|
92
|
+
if (nameNode instanceof StringLiteral && nameNode.getLiteralText() === prop.name) {
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
if (nameNode instanceof ComputedPropertyName) {
|
|
96
|
+
const expr = nameNode.getExpression();
|
|
97
|
+
if (expr instanceof NoSubstitutionTemplateLiteral && expr.getLiteralText() === prop.name) {
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return false;
|
|
102
|
+
});
|
|
103
|
+
if (!property) {
|
|
104
|
+
return { type: prop.type, optional: prop.nullable };
|
|
105
|
+
}
|
|
106
|
+
const tsType = property.getType();
|
|
107
|
+
const typeName = tsType.getText(property);
|
|
108
|
+
if (prop.enum && tsType.isEnum()) {
|
|
109
|
+
prop.items = tsType.getUnionTypes().map(t => t.getLiteralValueOrThrow());
|
|
110
|
+
}
|
|
111
|
+
if (tsType.isArray()) {
|
|
112
|
+
prop.array = true;
|
|
113
|
+
/* v8 ignore next */
|
|
114
|
+
if (tsType.getArrayElementType().isEnum()) {
|
|
115
|
+
prop.items = tsType
|
|
116
|
+
.getArrayElementType()
|
|
117
|
+
.getUnionTypes()
|
|
118
|
+
.map(t => t.getLiteralValueOrThrow());
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
let type = typeName;
|
|
122
|
+
const union = type.split(' | ');
|
|
123
|
+
const optional = property.hasQuestionToken?.() || union.includes('null') || union.includes('undefined') || tsType.isNullable();
|
|
124
|
+
type = union.filter(t => !['null', 'undefined'].includes(t)).join(' | ');
|
|
125
|
+
prop.array ??= type.endsWith('[]') || !!/Array<(.*)>/.exec(type);
|
|
126
|
+
if (prop.array && prop.enum) {
|
|
127
|
+
prop.enum = false;
|
|
128
|
+
}
|
|
129
|
+
type = type
|
|
130
|
+
.replace(/Array<(.*)>/, '$1') // unwrap array
|
|
131
|
+
.replace(/\[]$/, '') // remove array suffix
|
|
132
|
+
.replace(/\((.*)\)/, '$1'); // unwrap union types
|
|
133
|
+
// keep the array suffix in the type, it is needed in few places in discovery and comparator (`prop.array` is used only for enum arrays)
|
|
134
|
+
if (prop.array && !type.includes(' | ') && prop.kind === ReferenceKind.SCALAR) {
|
|
135
|
+
type += '[]';
|
|
136
|
+
}
|
|
137
|
+
return { type, optional };
|
|
171
138
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
139
|
+
getSourceFile(tsPath, validate) {
|
|
140
|
+
if (!this.sources) {
|
|
141
|
+
this.initSourceFiles();
|
|
142
|
+
}
|
|
143
|
+
const baseDir = this.config.get('baseDir');
|
|
144
|
+
const outDir = this.project.getCompilerOptions().outDir;
|
|
145
|
+
let path = tsPath;
|
|
146
|
+
/* v8 ignore next */
|
|
147
|
+
if (outDir != null) {
|
|
148
|
+
const outDirRelative = fs.relativePath(outDir, baseDir);
|
|
149
|
+
path = path.replace(new RegExp(`^${outDirRelative}`), '');
|
|
150
|
+
}
|
|
151
|
+
path = this.stripRelativePath(path);
|
|
152
|
+
const source = this.sources.find(s => s.getFilePath().endsWith(path));
|
|
153
|
+
if (!source && validate) {
|
|
154
|
+
throw new MetadataError(`Source file '${fs.relativePath(tsPath, baseDir)}' not found. Check your 'entitiesTs' option and verify you have 'compilerOptions.declaration' enabled in your 'tsconfig.json'. If you are using webpack, see https://bit.ly/35pPDNn`);
|
|
155
|
+
}
|
|
156
|
+
return source;
|
|
187
157
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
prop.ref = true;
|
|
158
|
+
stripRelativePath(str) {
|
|
159
|
+
return str.replace(/^(?:\.\.\/|\.\/)+/, '/');
|
|
191
160
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
this.project = new Project({
|
|
208
|
-
compilerOptions: {
|
|
209
|
-
strictNullChecks: true,
|
|
210
|
-
module: ModuleKind.Node20,
|
|
211
|
-
},
|
|
212
|
-
});
|
|
161
|
+
processWrapper(prop, wrapper) {
|
|
162
|
+
// type can be sometimes in form of:
|
|
163
|
+
// `'({ object?: Entity | undefined; } & import("...").Reference<Entity>)'`
|
|
164
|
+
// `{ object?: import("...").Entity | undefined; } & import("...").Reference<Entity>`
|
|
165
|
+
// `{ node?: ({ id?: number | undefined; } & import("...").Reference<import("...").Entity>) | undefined; } & import("...").Reference<Entity>`
|
|
166
|
+
// the regexp is looking for the `wrapper`, possible prefixed with `.` or wrapped in parens.
|
|
167
|
+
const type = prop.type.replace(/import\(.*\)\./g, '').replace(/\{ .* } & ([\w &]+)/g, '$1');
|
|
168
|
+
const m = new RegExp(`(?:^|[.( ])${wrapper}<(\\w+),?.*>(?:$|[) ])`).exec(type);
|
|
169
|
+
if (!m) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
prop.type = m[1];
|
|
173
|
+
if (['Ref', 'Reference', 'EntityRef', 'ScalarRef', 'ScalarReference'].includes(wrapper)) {
|
|
174
|
+
prop.ref = true;
|
|
175
|
+
}
|
|
213
176
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
177
|
+
initProject() {
|
|
178
|
+
/* v8 ignore next */
|
|
179
|
+
const tsConfigFilePath = this.config.get('discovery').tsConfigPath ?? './tsconfig.json';
|
|
180
|
+
try {
|
|
181
|
+
this.project = new Project({
|
|
182
|
+
tsConfigFilePath: fs.normalizePath(process.cwd(), tsConfigFilePath),
|
|
183
|
+
skipAddingFilesFromTsConfig: true,
|
|
184
|
+
compilerOptions: {
|
|
185
|
+
strictNullChecks: true,
|
|
186
|
+
module: ModuleKind.Node20,
|
|
187
|
+
},
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
catch (e) {
|
|
191
|
+
this.config.getLogger().warn('discovery', e.message);
|
|
192
|
+
this.project = new Project({
|
|
193
|
+
compilerOptions: {
|
|
194
|
+
strictNullChecks: true,
|
|
195
|
+
module: ModuleKind.Node20,
|
|
196
|
+
},
|
|
197
|
+
});
|
|
198
|
+
}
|
|
230
199
|
}
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
200
|
+
initSourceFiles() {
|
|
201
|
+
this.initProject();
|
|
202
|
+
this.sources = [];
|
|
203
|
+
// All entity files are first required during the discovery, before we reach here, so it is safe to get the parts from the global
|
|
204
|
+
// metadata storage. We know the path thanks to the decorators being executed. In case we are running the TS code, the extension
|
|
205
|
+
// will be already `.ts`, so no change is needed. `.js` files will get renamed to `.d.ts` files as they will be used as a source for
|
|
206
|
+
// the ts-morph reflection.
|
|
207
|
+
for (const meta of Utils.values(MetadataStorage.getMetadata())) {
|
|
208
|
+
const metaPath = fs.normalizePath(meta.path);
|
|
209
|
+
/* v8 ignore next */
|
|
210
|
+
const path = /\.[jt]s$/.exec(metaPath) ? metaPath.replace(/\.js$/, '.d.ts') : `${metaPath}.d.ts`; // when entities are bundled, their paths are just their names
|
|
211
|
+
const sourceFile = this.project.addSourceFileAtPathIfExists(path);
|
|
212
|
+
if (sourceFile) {
|
|
213
|
+
this.sources.push(sourceFile);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
235
216
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
Reflect.deleteProperty(
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
217
|
+
saveToCache(meta) {
|
|
218
|
+
if (!this.useCache()) {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
Reflect.deleteProperty(meta, 'root'); // to allow caching (as root can contain cycles)
|
|
222
|
+
const copy = Utils.copy(meta, false);
|
|
223
|
+
for (const prop of copy.props) {
|
|
224
|
+
if (Type.isMappedType(prop.type)) {
|
|
225
|
+
Reflect.deleteProperty(prop, 'type');
|
|
226
|
+
Reflect.deleteProperty(prop, 'customType');
|
|
227
|
+
}
|
|
228
|
+
if (prop.default) {
|
|
229
|
+
const raw = RawQueryFragment.getKnownFragment(prop.default);
|
|
230
|
+
if (raw) {
|
|
231
|
+
prop.defaultRaw ??= this.config.getPlatform().formatQuery(raw.sql, raw.params);
|
|
232
|
+
Reflect.deleteProperty(prop, 'default');
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
Reflect.deleteProperty(prop, 'targetMeta');
|
|
236
|
+
}
|
|
237
|
+
[
|
|
238
|
+
'prototype',
|
|
239
|
+
'props',
|
|
240
|
+
'referencingProperties',
|
|
241
|
+
'propertyOrder',
|
|
242
|
+
'relations',
|
|
243
|
+
'concurrencyCheckKeys',
|
|
244
|
+
'checks',
|
|
245
|
+
].forEach(key => delete copy[key]);
|
|
246
|
+
// base entity without properties might not have path, but nothing to cache there
|
|
247
|
+
if (meta.path) {
|
|
248
|
+
meta.path = fs.relativePath(meta.path, this.config.get('baseDir'));
|
|
249
|
+
this.config.getMetadataCacheAdapter().set(this.getCacheKey(meta), copy, meta.path);
|
|
248
250
|
}
|
|
249
|
-
}
|
|
250
|
-
Reflect.deleteProperty(prop, 'targetMeta');
|
|
251
251
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
'referencingProperties',
|
|
256
|
-
'propertyOrder',
|
|
257
|
-
'relations',
|
|
258
|
-
'concurrencyCheckKeys',
|
|
259
|
-
'checks',
|
|
260
|
-
].forEach(key => delete copy[key]);
|
|
261
|
-
// base entity without properties might not have path, but nothing to cache there
|
|
262
|
-
if (meta.path) {
|
|
263
|
-
meta.path = fs.relativePath(meta.path, this.config.get('baseDir'));
|
|
264
|
-
this.config.getMetadataCacheAdapter().set(this.getCacheKey(meta), copy, meta.path);
|
|
252
|
+
getCacheKey(meta) {
|
|
253
|
+
/* v8 ignore next */
|
|
254
|
+
return meta.className + (meta.path ? extname(meta.path) : '');
|
|
265
255
|
}
|
|
266
|
-
}
|
|
267
|
-
getCacheKey(meta) {
|
|
268
|
-
/* v8 ignore next */
|
|
269
|
-
return meta.className + (meta.path ? extname(meta.path) : '');
|
|
270
|
-
}
|
|
271
256
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mikro-orm/reflection",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.5-dev.1",
|
|
4
4
|
"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.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"data-mapper",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"@mikro-orm/core": "^7.0.4"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
|
-
"@mikro-orm/core": "7.0.
|
|
56
|
+
"@mikro-orm/core": "7.0.5-dev.1"
|
|
57
57
|
},
|
|
58
58
|
"engines": {
|
|
59
59
|
"node": ">= 22.17.0"
|