@mikro-orm/reflection 7.0.0-dev.11 → 7.0.0-dev.110
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 +3 -2
- package/TsMorphMetadataProvider.d.ts +7 -2
- package/TsMorphMetadataProvider.js +96 -34
- package/package.json +5 -6
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).
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { type SourceFile } from 'ts-morph';
|
|
2
|
-
import {
|
|
2
|
+
import { type EntityMetadata, MetadataProvider } from '@mikro-orm/core';
|
|
3
3
|
export declare class TsMorphMetadataProvider extends MetadataProvider {
|
|
4
4
|
private project;
|
|
5
5
|
private sources;
|
|
6
|
+
static useCache(): boolean;
|
|
6
7
|
useCache(): boolean;
|
|
7
|
-
loadEntityMetadata(meta: EntityMetadata
|
|
8
|
+
loadEntityMetadata(meta: EntityMetadata): void;
|
|
8
9
|
getExistingSourceFile(path: string, ext?: string, validate?: boolean): SourceFile;
|
|
9
10
|
protected initProperties(meta: EntityMetadata): void;
|
|
10
11
|
private extractType;
|
|
@@ -12,7 +13,11 @@ export declare class TsMorphMetadataProvider extends MetadataProvider {
|
|
|
12
13
|
private initPropertyType;
|
|
13
14
|
private readTypeFromSource;
|
|
14
15
|
private getSourceFile;
|
|
16
|
+
private stripRelativePath;
|
|
15
17
|
private processWrapper;
|
|
16
18
|
private initProject;
|
|
17
19
|
private initSourceFiles;
|
|
20
|
+
loadFromCache(meta: EntityMetadata, cache: EntityMetadata): void;
|
|
21
|
+
saveToCache(meta: EntityMetadata): void;
|
|
22
|
+
getCacheKey(meta: Pick<EntityMetadata, 'className' | 'path'>): string;
|
|
18
23
|
}
|
|
@@ -1,12 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { extname } from 'node:path';
|
|
2
|
+
import { ComputedPropertyName, ModuleKind, NoSubstitutionTemplateLiteral, Project, StringLiteral } from 'ts-morph';
|
|
3
|
+
import { MetadataError, MetadataProvider, MetadataStorage, RawQueryFragment, ReferenceKind, Type, Utils, } from '@mikro-orm/core';
|
|
4
|
+
import { fs } from '@mikro-orm/core/fs-utils';
|
|
3
5
|
export class TsMorphMetadataProvider extends MetadataProvider {
|
|
4
6
|
project;
|
|
5
7
|
sources;
|
|
8
|
+
static useCache() {
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
6
11
|
useCache() {
|
|
7
|
-
return this.config.get('metadataCache').enabled ??
|
|
12
|
+
return this.config.get('metadataCache').enabled ?? TsMorphMetadataProvider.useCache();
|
|
8
13
|
}
|
|
9
|
-
loadEntityMetadata(meta
|
|
14
|
+
loadEntityMetadata(meta) {
|
|
10
15
|
if (!meta.path) {
|
|
11
16
|
return;
|
|
12
17
|
}
|
|
@@ -23,14 +28,12 @@ export class TsMorphMetadataProvider extends MetadataProvider {
|
|
|
23
28
|
// load types and column names
|
|
24
29
|
for (const prop of Object.values(meta.properties)) {
|
|
25
30
|
const type = this.extractType(prop);
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
prop.type = type || prop.type;
|
|
31
|
+
this.initPropertyType(meta, prop);
|
|
32
|
+
prop.type = type ?? prop.type;
|
|
30
33
|
}
|
|
31
34
|
}
|
|
32
35
|
extractType(prop) {
|
|
33
|
-
if (
|
|
36
|
+
if (typeof prop.entity === 'string') {
|
|
34
37
|
return prop.entity;
|
|
35
38
|
}
|
|
36
39
|
if (prop.entity) {
|
|
@@ -39,12 +42,11 @@ export class TsMorphMetadataProvider extends MetadataProvider {
|
|
|
39
42
|
return prop.type;
|
|
40
43
|
}
|
|
41
44
|
cleanUpTypeTags(type) {
|
|
42
|
-
const genericTags = [/Opt<(.*?)>/, /Hidden<(.*?)>/];
|
|
45
|
+
const genericTags = [/Opt<(.*?)>/, /Hidden<(.*?)>/, /RequiredNullable<(.*?)>/];
|
|
43
46
|
const intersectionTags = [
|
|
44
|
-
'
|
|
45
|
-
'
|
|
46
|
-
'
|
|
47
|
-
'{ [__hidden]?: 1; }',
|
|
47
|
+
'Opt.Brand',
|
|
48
|
+
'Hidden.Brand',
|
|
49
|
+
'RequiredNullable.Brand',
|
|
48
50
|
];
|
|
49
51
|
for (const tag of genericTags) {
|
|
50
52
|
type = type.replace(tag, '$1');
|
|
@@ -57,17 +59,17 @@ export class TsMorphMetadataProvider extends MetadataProvider {
|
|
|
57
59
|
}
|
|
58
60
|
initPropertyType(meta, prop) {
|
|
59
61
|
const { type: typeRaw, optional } = this.readTypeFromSource(meta, prop);
|
|
60
|
-
|
|
61
|
-
prop.type = type;
|
|
62
|
-
prop.runtimeType = type;
|
|
62
|
+
prop.type = this.cleanUpTypeTags(typeRaw);
|
|
63
63
|
if (optional) {
|
|
64
64
|
prop.optional = true;
|
|
65
65
|
}
|
|
66
66
|
this.processWrapper(prop, 'Ref');
|
|
67
67
|
this.processWrapper(prop, 'Reference');
|
|
68
|
+
this.processWrapper(prop, 'EntityRef');
|
|
69
|
+
this.processWrapper(prop, 'ScalarRef');
|
|
68
70
|
this.processWrapper(prop, 'ScalarReference');
|
|
69
|
-
this.processWrapper(prop, 'Ref');
|
|
70
71
|
this.processWrapper(prop, 'Collection');
|
|
72
|
+
prop.runtimeType ??= prop.type;
|
|
71
73
|
if (prop.type.replace(/import\(.*\)\./g, '').match(/^(Dictionary|Record)<.*>$/)) {
|
|
72
74
|
prop.type = 'json';
|
|
73
75
|
}
|
|
@@ -75,12 +77,27 @@ export class TsMorphMetadataProvider extends MetadataProvider {
|
|
|
75
77
|
readTypeFromSource(meta, prop) {
|
|
76
78
|
const source = this.getExistingSourceFile(meta.path);
|
|
77
79
|
const cls = source.getClass(meta.className);
|
|
78
|
-
/* v8 ignore next
|
|
80
|
+
/* v8 ignore next */
|
|
79
81
|
if (!cls) {
|
|
80
82
|
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`);
|
|
81
83
|
}
|
|
82
84
|
const properties = cls.getInstanceProperties();
|
|
83
|
-
const property = properties.find(v =>
|
|
85
|
+
const property = properties.find(v => {
|
|
86
|
+
if (v.getName() === prop.name) {
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
const nameNode = v.getNameNode();
|
|
90
|
+
if (nameNode instanceof StringLiteral && nameNode.getLiteralText() === prop.name) {
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
if (nameNode instanceof ComputedPropertyName) {
|
|
94
|
+
const expr = nameNode.getExpression();
|
|
95
|
+
if (expr instanceof NoSubstitutionTemplateLiteral && expr.getLiteralText() === prop.name) {
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return false;
|
|
100
|
+
});
|
|
84
101
|
if (!property) {
|
|
85
102
|
return { type: prop.type, optional: prop.nullable };
|
|
86
103
|
}
|
|
@@ -91,7 +108,7 @@ export class TsMorphMetadataProvider extends MetadataProvider {
|
|
|
91
108
|
}
|
|
92
109
|
if (tsType.isArray()) {
|
|
93
110
|
prop.array = true;
|
|
94
|
-
/* v8 ignore next
|
|
111
|
+
/* v8 ignore next */
|
|
95
112
|
if (tsType.getArrayElementType().isEnum()) {
|
|
96
113
|
prop.items = tsType.getArrayElementType().getUnionTypes().map(t => t.getLiteralValueOrThrow());
|
|
97
114
|
}
|
|
@@ -121,17 +138,21 @@ export class TsMorphMetadataProvider extends MetadataProvider {
|
|
|
121
138
|
const baseDir = this.config.get('baseDir');
|
|
122
139
|
const outDir = this.project.getCompilerOptions().outDir;
|
|
123
140
|
let path = tsPath;
|
|
141
|
+
/* v8 ignore next */
|
|
124
142
|
if (outDir != null) {
|
|
125
|
-
const outDirRelative =
|
|
143
|
+
const outDirRelative = fs.relativePath(outDir, baseDir);
|
|
126
144
|
path = path.replace(new RegExp(`^${outDirRelative}`), '');
|
|
127
145
|
}
|
|
128
|
-
path =
|
|
146
|
+
path = this.stripRelativePath(path);
|
|
129
147
|
const source = this.sources.find(s => s.getFilePath().endsWith(path));
|
|
130
148
|
if (!source && validate) {
|
|
131
|
-
throw new MetadataError(`Source file '${tsPath}' 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`);
|
|
149
|
+
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`);
|
|
132
150
|
}
|
|
133
151
|
return source;
|
|
134
152
|
}
|
|
153
|
+
stripRelativePath(str) {
|
|
154
|
+
return str.replace(/^(?:\.\.\/|\.\/)+/, '/');
|
|
155
|
+
}
|
|
135
156
|
processWrapper(prop, wrapper) {
|
|
136
157
|
// type can be sometimes in form of:
|
|
137
158
|
// `'({ object?: Entity | undefined; } & import("...").Reference<Entity>)'`
|
|
@@ -146,21 +167,20 @@ export class TsMorphMetadataProvider extends MetadataProvider {
|
|
|
146
167
|
return;
|
|
147
168
|
}
|
|
148
169
|
prop.type = m[1];
|
|
149
|
-
if (['Ref', 'Reference', '
|
|
170
|
+
if (['Ref', 'Reference', 'EntityRef', 'ScalarRef', 'ScalarReference'].includes(wrapper)) {
|
|
150
171
|
prop.ref = true;
|
|
151
172
|
}
|
|
152
173
|
}
|
|
153
174
|
initProject() {
|
|
154
|
-
const settings = ConfigurationLoader.getSettings();
|
|
155
175
|
/* v8 ignore next */
|
|
156
|
-
const tsConfigFilePath = this.config.get('discovery').tsConfigPath ??
|
|
176
|
+
const tsConfigFilePath = this.config.get('discovery').tsConfigPath ?? './tsconfig.json';
|
|
157
177
|
try {
|
|
158
178
|
this.project = new Project({
|
|
159
|
-
tsConfigFilePath:
|
|
179
|
+
tsConfigFilePath: fs.normalizePath(process.cwd(), tsConfigFilePath),
|
|
160
180
|
skipAddingFilesFromTsConfig: true,
|
|
161
181
|
compilerOptions: {
|
|
162
182
|
strictNullChecks: true,
|
|
163
|
-
module: ModuleKind.
|
|
183
|
+
module: ModuleKind.Node20,
|
|
164
184
|
},
|
|
165
185
|
});
|
|
166
186
|
}
|
|
@@ -169,22 +189,20 @@ export class TsMorphMetadataProvider extends MetadataProvider {
|
|
|
169
189
|
this.project = new Project({
|
|
170
190
|
compilerOptions: {
|
|
171
191
|
strictNullChecks: true,
|
|
172
|
-
module: ModuleKind.
|
|
192
|
+
module: ModuleKind.Node20,
|
|
173
193
|
},
|
|
174
194
|
});
|
|
175
195
|
}
|
|
176
196
|
}
|
|
177
197
|
initSourceFiles() {
|
|
178
|
-
|
|
179
|
-
this.initProject();
|
|
180
|
-
}
|
|
198
|
+
this.initProject();
|
|
181
199
|
this.sources = [];
|
|
182
200
|
// All entity files are first required during the discovery, before we reach here, so it is safe to get the parts from the global
|
|
183
201
|
// metadata storage. We know the path thanks to the decorators being executed. In case we are running the TS code, the extension
|
|
184
202
|
// 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
|
|
185
203
|
// the ts-morph reflection.
|
|
186
204
|
for (const meta of Utils.values(MetadataStorage.getMetadata())) {
|
|
187
|
-
/* v8 ignore next
|
|
205
|
+
/* v8 ignore next */
|
|
188
206
|
const path = meta.path.match(/\.[jt]s$/)
|
|
189
207
|
? meta.path.replace(/\.js$/, '.d.ts')
|
|
190
208
|
: `${meta.path}.d.ts`; // when entities are bundled, their paths are just their names
|
|
@@ -194,4 +212,48 @@ export class TsMorphMetadataProvider extends MetadataProvider {
|
|
|
194
212
|
}
|
|
195
213
|
}
|
|
196
214
|
}
|
|
215
|
+
loadFromCache(meta, cache) {
|
|
216
|
+
Object.values(cache.properties).forEach(prop => {
|
|
217
|
+
const metaProp = meta.properties[prop.name];
|
|
218
|
+
/* v8 ignore next 3 */
|
|
219
|
+
if (metaProp?.enum && Array.isArray(metaProp.items)) {
|
|
220
|
+
delete prop.items;
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
Utils.mergeConfig(meta, cache);
|
|
224
|
+
}
|
|
225
|
+
saveToCache(meta) {
|
|
226
|
+
if (!this.useCache()) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
Reflect.deleteProperty(meta, 'root'); // to allow caching (as root can contain cycles)
|
|
230
|
+
const copy = Utils.copy(meta, false);
|
|
231
|
+
for (const prop of copy.props) {
|
|
232
|
+
if (Type.isMappedType(prop.type)) {
|
|
233
|
+
Reflect.deleteProperty(prop, 'type');
|
|
234
|
+
Reflect.deleteProperty(prop, 'customType');
|
|
235
|
+
}
|
|
236
|
+
if (prop.default) {
|
|
237
|
+
const raw = RawQueryFragment.getKnownFragment(prop.default);
|
|
238
|
+
if (raw) {
|
|
239
|
+
prop.defaultRaw ??= this.config.getPlatform().formatQuery(raw.sql, raw.params);
|
|
240
|
+
Reflect.deleteProperty(prop, 'default');
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
Reflect.deleteProperty(prop, 'targetMeta');
|
|
244
|
+
}
|
|
245
|
+
[
|
|
246
|
+
'prototype', 'props', 'referencingProperties', 'propertyOrder', 'relations',
|
|
247
|
+
'concurrencyCheckKeys', 'checks',
|
|
248
|
+
].forEach(key => delete copy[key]);
|
|
249
|
+
// base entity without properties might not have path, but nothing to cache there
|
|
250
|
+
if (meta.path) {
|
|
251
|
+
meta.path = fs.relativePath(meta.path, this.config.get('baseDir'));
|
|
252
|
+
this.config.getMetadataCacheAdapter().set(this.getCacheKey(meta), copy, meta.path);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
getCacheKey(meta) {
|
|
256
|
+
/* v8 ignore next */
|
|
257
|
+
return meta.className + (meta.path ? extname(meta.path) : '');
|
|
258
|
+
}
|
|
197
259
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mikro-orm/reflection",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "7.0.0-dev.
|
|
4
|
+
"version": "7.0.0-dev.110",
|
|
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,13 +50,12 @@
|
|
|
50
50
|
"access": "public"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"
|
|
54
|
-
"ts-morph": "25.0.1"
|
|
53
|
+
"ts-morph": "27.0.2"
|
|
55
54
|
},
|
|
56
55
|
"devDependencies": {
|
|
57
|
-
"@mikro-orm/core": "^6.
|
|
56
|
+
"@mikro-orm/core": "^6.6.2"
|
|
58
57
|
},
|
|
59
58
|
"peerDependencies": {
|
|
60
|
-
"@mikro-orm/core": "7.0.0-dev.
|
|
59
|
+
"@mikro-orm/core": "7.0.0-dev.110"
|
|
61
60
|
}
|
|
62
61
|
}
|