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