@mikro-orm/reflection 7.0.0-dev.9 → 7.0.0-dev.91

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 CHANGED
@@ -11,7 +11,6 @@ TypeScript ORM for Node.js based on Data Mapper, [Unit of Work](https://mikro-or
11
11
  [![Chat on discord](https://img.shields.io/discord/1214904142443839538?label=discord&color=blue)](https://discord.gg/w8bjxFHS7X)
12
12
  [![Downloads](https://img.shields.io/npm/dm/@mikro-orm/core.svg)](https://www.npmjs.com/package/@mikro-orm/core)
13
13
  [![Coverage Status](https://img.shields.io/coveralls/mikro-orm/mikro-orm.svg)](https://coveralls.io/r/mikro-orm/mikro-orm?branch=master)
14
- [![Maintainability](https://api.codeclimate.com/v1/badges/27999651d3adc47cfa40/maintainability)](https://codeclimate.com/github/mikro-orm/mikro-orm/maintainability)
15
14
  [![Build Status](https://github.com/mikro-orm/mikro-orm/workflows/tests/badge.svg?branch=master)](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
- - [Preloading Deeply Nested Structures via populate](https://mikro-orm.io/docs/nested-populate)
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 { MetadataProvider, type EntityMetadata } from '@mikro-orm/core';
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, name: string): void;
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,16 @@
1
- import { ModuleKind, Project } from 'ts-morph';
2
- import { MetadataError, MetadataProvider, MetadataStorage, ReferenceKind, Utils, ConfigurationLoader, } from '@mikro-orm/core';
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';
3
4
  export class TsMorphMetadataProvider extends MetadataProvider {
4
5
  project;
5
6
  sources;
7
+ static useCache() {
8
+ return true;
9
+ }
6
10
  useCache() {
7
- return this.config.get('metadataCache').enabled ?? true;
11
+ return this.config.get('metadataCache').enabled ?? TsMorphMetadataProvider.useCache();
8
12
  }
9
- loadEntityMetadata(meta, name) {
13
+ loadEntityMetadata(meta) {
10
14
  if (!meta.path) {
11
15
  return;
12
16
  }
@@ -23,14 +27,12 @@ export class TsMorphMetadataProvider extends MetadataProvider {
23
27
  // load types and column names
24
28
  for (const prop of Object.values(meta.properties)) {
25
29
  const type = this.extractType(prop);
26
- if (!type || this.config.get('discovery').alwaysAnalyseProperties) {
27
- this.initPropertyType(meta, prop);
28
- }
29
- prop.type = type || prop.type;
30
+ this.initPropertyType(meta, prop);
31
+ prop.type = type ?? prop.type;
30
32
  }
31
33
  }
32
34
  extractType(prop) {
33
- if (Utils.isString(prop.entity)) {
35
+ if (typeof prop.entity === 'string') {
34
36
  return prop.entity;
35
37
  }
36
38
  if (prop.entity) {
@@ -39,12 +41,11 @@ export class TsMorphMetadataProvider extends MetadataProvider {
39
41
  return prop.type;
40
42
  }
41
43
  cleanUpTypeTags(type) {
42
- const genericTags = [/Opt<(.*?)>/, /Hidden<(.*?)>/];
44
+ const genericTags = [/Opt<(.*?)>/, /Hidden<(.*?)>/, /RequiredNullable<(.*?)>/];
43
45
  const intersectionTags = [
44
- '{ [__optional]?: 1 | undefined; }',
45
- '{ [__optional]?: 1; }',
46
- '{ [__hidden]?: 1 | undefined; }',
47
- '{ [__hidden]?: 1; }',
46
+ 'Opt.Brand',
47
+ 'Hidden.Brand',
48
+ 'RequiredNullable.Brand',
48
49
  ];
49
50
  for (const tag of genericTags) {
50
51
  type = type.replace(tag, '$1');
@@ -57,17 +58,17 @@ export class TsMorphMetadataProvider extends MetadataProvider {
57
58
  }
58
59
  initPropertyType(meta, prop) {
59
60
  const { type: typeRaw, optional } = this.readTypeFromSource(meta, prop);
60
- const type = this.cleanUpTypeTags(typeRaw);
61
- prop.type = type;
62
- prop.runtimeType = type;
61
+ prop.type = this.cleanUpTypeTags(typeRaw);
63
62
  if (optional) {
64
63
  prop.optional = true;
65
64
  }
66
65
  this.processWrapper(prop, 'Ref');
67
66
  this.processWrapper(prop, 'Reference');
67
+ this.processWrapper(prop, 'EntityRef');
68
+ this.processWrapper(prop, 'ScalarRef');
68
69
  this.processWrapper(prop, 'ScalarReference');
69
- this.processWrapper(prop, 'Ref');
70
70
  this.processWrapper(prop, 'Collection');
71
+ prop.runtimeType ??= prop.type;
71
72
  if (prop.type.replace(/import\(.*\)\./g, '').match(/^(Dictionary|Record)<.*>$/)) {
72
73
  prop.type = 'json';
73
74
  }
@@ -75,12 +76,27 @@ export class TsMorphMetadataProvider extends MetadataProvider {
75
76
  readTypeFromSource(meta, prop) {
76
77
  const source = this.getExistingSourceFile(meta.path);
77
78
  const cls = source.getClass(meta.className);
78
- /* v8 ignore next 3 */
79
+ /* v8 ignore next */
79
80
  if (!cls) {
80
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`);
81
82
  }
82
83
  const properties = cls.getInstanceProperties();
83
- const property = properties.find(v => v.getName() === prop.name);
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
+ });
84
100
  if (!property) {
85
101
  return { type: prop.type, optional: prop.nullable };
86
102
  }
@@ -91,7 +107,7 @@ export class TsMorphMetadataProvider extends MetadataProvider {
91
107
  }
92
108
  if (tsType.isArray()) {
93
109
  prop.array = true;
94
- /* v8 ignore next 3 */
110
+ /* v8 ignore next */
95
111
  if (tsType.getArrayElementType().isEnum()) {
96
112
  prop.items = tsType.getArrayElementType().getUnionTypes().map(t => t.getLiteralValueOrThrow());
97
113
  }
@@ -121,17 +137,21 @@ export class TsMorphMetadataProvider extends MetadataProvider {
121
137
  const baseDir = this.config.get('baseDir');
122
138
  const outDir = this.project.getCompilerOptions().outDir;
123
139
  let path = tsPath;
140
+ /* v8 ignore next */
124
141
  if (outDir != null) {
125
142
  const outDirRelative = Utils.relativePath(outDir, baseDir);
126
143
  path = path.replace(new RegExp(`^${outDirRelative}`), '');
127
144
  }
128
- path = Utils.stripRelativePath(path);
145
+ path = this.stripRelativePath(path);
129
146
  const source = this.sources.find(s => s.getFilePath().endsWith(path));
130
147
  if (!source && validate) {
131
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`);
132
149
  }
133
150
  return source;
134
151
  }
152
+ stripRelativePath(str) {
153
+ return str.replace(/^(?:\.\.\/|\.\/)+/, '/');
154
+ }
135
155
  processWrapper(prop, wrapper) {
136
156
  // type can be sometimes in form of:
137
157
  // `'({ object?: Entity | undefined; } & import("...").Reference<Entity>)'`
@@ -146,21 +166,20 @@ export class TsMorphMetadataProvider extends MetadataProvider {
146
166
  return;
147
167
  }
148
168
  prop.type = m[1];
149
- if (['Ref', 'Reference', 'Ref'].includes(wrapper)) {
169
+ if (['Ref', 'Reference', 'EntityRef', 'ScalarRef', 'ScalarReference'].includes(wrapper)) {
150
170
  prop.ref = true;
151
171
  }
152
172
  }
153
173
  initProject() {
154
- const settings = ConfigurationLoader.getSettings();
155
174
  /* v8 ignore next */
156
- const tsConfigFilePath = this.config.get('discovery').tsConfigPath ?? settings.tsConfigPath ?? './tsconfig.json';
175
+ const tsConfigFilePath = this.config.get('discovery').tsConfigPath ?? './tsconfig.json';
157
176
  try {
158
177
  this.project = new Project({
159
178
  tsConfigFilePath: Utils.normalizePath(process.cwd(), tsConfigFilePath),
160
179
  skipAddingFilesFromTsConfig: true,
161
180
  compilerOptions: {
162
181
  strictNullChecks: true,
163
- module: ModuleKind.Node16,
182
+ module: ModuleKind.Node20,
164
183
  },
165
184
  });
166
185
  }
@@ -169,22 +188,20 @@ export class TsMorphMetadataProvider extends MetadataProvider {
169
188
  this.project = new Project({
170
189
  compilerOptions: {
171
190
  strictNullChecks: true,
172
- module: ModuleKind.Node16,
191
+ module: ModuleKind.Node20,
173
192
  },
174
193
  });
175
194
  }
176
195
  }
177
196
  initSourceFiles() {
178
- if (!this.project) {
179
- this.initProject();
180
- }
197
+ this.initProject();
181
198
  this.sources = [];
182
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
183
200
  // metadata storage. We know the path thanks to the decorators being executed. In case we are running the TS code, the extension
184
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
185
202
  // the ts-morph reflection.
186
203
  for (const meta of Utils.values(MetadataStorage.getMetadata())) {
187
- /* v8 ignore next 3 */
204
+ /* v8 ignore next */
188
205
  const path = meta.path.match(/\.[jt]s$/)
189
206
  ? meta.path.replace(/\.js$/, '.d.ts')
190
207
  : `${meta.path}.d.ts`; // when entities are bundled, their paths are just their names
@@ -194,4 +211,47 @@ export class TsMorphMetadataProvider extends MetadataProvider {
194
211
  }
195
212
  }
196
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
+ }
197
257
  }
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.9",
4
+ "version": "7.0.0-dev.91",
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.11.0"
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
- "globby": "11.1.0",
54
- "ts-morph": "25.0.1"
53
+ "ts-morph": "27.0.2"
55
54
  },
56
55
  "devDependencies": {
57
- "@mikro-orm/core": "^6.4.13"
56
+ "@mikro-orm/core": "^6.6.2"
58
57
  },
59
58
  "peerDependencies": {
60
- "@mikro-orm/core": "7.0.0-dev.9"
59
+ "@mikro-orm/core": "7.0.0-dev.91"
61
60
  }
62
61
  }