@mikro-orm/core 7.0.0-dev.60 → 7.0.0-dev.62

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/MikroORM.js CHANGED
@@ -39,7 +39,6 @@ export class MikroORM {
39
39
  * - no support for folder based discovery
40
40
  */
41
41
  constructor(options) {
42
- ConfigurationLoader.registerDotenv(options);
43
42
  const env = ConfigurationLoader.loadEnvironmentVarsSync();
44
43
  const coreVersion = ConfigurationLoader.checkPackageVersion();
45
44
  options = Utils.merge(options, env);
@@ -1,6 +1,8 @@
1
1
  import type { AnyString, Dictionary, EntityClass } from '../typings.js';
2
2
  export declare function Embeddable<T>(options?: EmbeddableOptions<T>): (target: T) => T;
3
3
  export interface EmbeddableOptions<T> {
4
+ /** Specify constructor parameters to be used in `em.create` or when `forceConstructor` is enabled. Those should be names of declared entity properties in the same order as your constructor uses them. The ORM tries to infer those automatically, use this option in case the inference fails. */
5
+ constructorParams?: (T extends EntityClass<infer P> ? keyof P : string)[];
4
6
  discriminatorColumn?: (T extends EntityClass<infer P> ? keyof P : string) | AnyString;
5
7
  discriminatorMap?: Dictionary<string>;
6
8
  discriminatorValue?: number | string;
@@ -16,6 +16,8 @@ export type EntityOptions<T, E = T extends EntityClass<infer P> ? P : T> = {
16
16
  discriminatorValue?: number | string;
17
17
  /** Enforce use of constructor when creating managed entity instances. */
18
18
  forceConstructor?: boolean;
19
+ /** Specify constructor parameters to be used in `em.create` or when `forceConstructor` is enabled. Those should be names of declared entity properties in the same order as your constructor uses them. The ORM tries to infer those automatically, use this option in case the inference fails. */
20
+ constructorParams?: (T extends EntityClass<infer P> ? keyof P : string)[];
19
21
  /** Specify comment to table. (SQL only) */
20
22
  comment?: string;
21
23
  /** Marks entity as abstract, such entities are inlined during discovery. */
@@ -32,9 +32,7 @@ export class ArrayCollection {
32
32
  if (this.items.size === 0) {
33
33
  return [];
34
34
  }
35
- const meta = this.property.targetMeta;
36
- const args = meta.toJsonParams.map(() => undefined);
37
- return this.map(item => wrap(item).toJSON(...args));
35
+ return this.map(item => wrap(item).toJSON());
38
36
  }
39
37
  toJSON() {
40
38
  return this.toArray();
@@ -60,7 +60,7 @@ export class EntityFactory {
60
60
  wrapped.__initialized = options.initialized;
61
61
  if (options.newEntity || meta.forceConstructor || meta.virtual) {
62
62
  const tmp = { ...data };
63
- meta.constructorParams.forEach(prop => delete tmp[prop]);
63
+ meta.constructorParams?.forEach(prop => delete tmp[prop]);
64
64
  this.hydrate(entity, meta2, tmp, options);
65
65
  // since we now process only a copy of the `data` via hydrator, but later we register the state with the full snapshot,
66
66
  // we need to go through all props with custom types that have `ensureComparable: true` and ensure they are comparable
@@ -299,32 +299,37 @@ export class EntityFactory {
299
299
  * returns parameters for entity constructor, creating references from plain ids
300
300
  */
301
301
  extractConstructorParams(meta, data, options) {
302
+ if (!meta.constructorParams) {
303
+ return [data];
304
+ }
302
305
  return meta.constructorParams.map(k => {
303
- if (meta.properties[k] && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(meta.properties[k].kind) && data[k]) {
304
- const pk = Reference.unwrapReference(data[k]);
305
- const entity = this.unitOfWork.getById(meta.properties[k].type, pk, options.schema, true);
306
+ const prop = meta.properties[k];
307
+ const value = data[k];
308
+ if (prop && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && value) {
309
+ const pk = Reference.unwrapReference(value);
310
+ const entity = this.unitOfWork.getById(prop.type, pk, options.schema, true);
306
311
  if (entity) {
307
312
  return entity;
308
313
  }
309
- if (Utils.isEntity(data[k])) {
310
- return data[k];
314
+ if (Utils.isEntity(value)) {
315
+ return value;
311
316
  }
312
- const nakedPk = Utils.extractPK(data[k], meta.properties[k].targetMeta, true);
313
- if (Utils.isObject(data[k]) && !nakedPk) {
314
- return this.create(meta.properties[k].type, data[k], options);
317
+ const nakedPk = Utils.extractPK(value, prop.targetMeta, true);
318
+ if (Utils.isObject(value) && !nakedPk) {
319
+ return this.create(prop.type, value, options);
315
320
  }
316
321
  const { newEntity, initialized, ...rest } = options;
317
- const target = this.createReference(meta.properties[k].type, nakedPk, rest);
318
- return Reference.wrapReference(target, meta.properties[k]);
322
+ const target = this.createReference(prop.type, nakedPk, rest);
323
+ return Reference.wrapReference(target, prop);
319
324
  }
320
- if (meta.properties[k]?.kind === ReferenceKind.EMBEDDED && data[k]) {
325
+ if (prop?.kind === ReferenceKind.EMBEDDED && value) {
321
326
  /* v8 ignore next 3 */
322
- if (Utils.isEntity(data[k])) {
323
- return data[k];
327
+ if (Utils.isEntity(value)) {
328
+ return value;
324
329
  }
325
- return this.createEmbeddable(meta.properties[k].type, data[k], options);
330
+ return this.createEmbeddable(prop.type, value, options);
326
331
  }
327
- if (!meta.properties[k]) {
332
+ if (!prop) {
328
333
  const tmp = { ...data };
329
334
  for (const prop of meta.props) {
330
335
  if (!options.convertCustomTypes || !prop.customType || tmp[prop.name] == null) {
@@ -339,10 +344,10 @@ export class EntityFactory {
339
344
  }
340
345
  return tmp;
341
346
  }
342
- if (options.convertCustomTypes && meta.properties[k].customType && data[k] != null) {
343
- return meta.properties[k].customType.convertToJSValue(data[k], this.platform);
347
+ if (options.convertCustomTypes && prop.customType && value != null) {
348
+ return prop.customType.convertToJSValue(value, this.platform);
344
349
  }
345
- return data[k];
350
+ return value;
346
351
  });
347
352
  }
348
353
  get unitOfWork() {
@@ -32,7 +32,7 @@ export class EntityHelper {
32
32
  const prototype = meta.prototype;
33
33
  if (!prototype.toJSON) { // toJSON can be overridden
34
34
  prototype.toJSON = function (...args) {
35
- return EntityTransformer.toObject(this, ...args.slice(meta.toJsonParams.length));
35
+ return EntityTransformer.toObject(this, ...args);
36
36
  };
37
37
  }
38
38
  }
@@ -157,9 +157,7 @@ export class EntitySchema {
157
157
  this._meta.prototype = proto.prototype;
158
158
  this._meta.className = proto.name;
159
159
  if (!sameClass || !this._meta.constructorParams) {
160
- const tokens = Utils.tokenize(proto);
161
- this._meta.constructorParams = Utils.getParamNames(tokens, 'constructor');
162
- this._meta.toJsonParams = Utils.getParamNames(tokens, 'toJSON').filter(p => p !== '...args');
160
+ this._meta.constructorParams = Utils.getConstructorParams(proto);
163
161
  }
164
162
  if (!this.internal) {
165
163
  EntitySchema.REGISTRY.set(proto, this);
@@ -731,12 +731,9 @@ export class MetadataDiscovery {
731
731
  Utils.keys(base.hooks).forEach(type => {
732
732
  meta.hooks[type] = Utils.unique([...base.hooks[type], ...(meta.hooks[type] || [])]);
733
733
  });
734
- if (meta.constructorParams.length === 0 && base.constructorParams.length > 0) {
734
+ if ((meta.constructorParams?.length ?? 0) === 0 && (base.constructorParams?.length ?? 0) > 0) {
735
735
  meta.constructorParams = [...base.constructorParams];
736
736
  }
737
- if (meta.toJsonParams.length === 0 && base.toJsonParams.length > 0) {
738
- meta.toJsonParams = [...base.toJsonParams];
739
- }
740
737
  return order;
741
738
  }
742
739
  initPolyEmbeddables(embeddedProp, discovered, visited = new Set()) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mikro-orm/core",
3
3
  "type": "module",
4
- "version": "7.0.0-dev.60",
4
+ "version": "7.0.0-dev.62",
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",
@@ -52,9 +52,7 @@
52
52
  "access": "public"
53
53
  },
54
54
  "dependencies": {
55
- "dotenv": "17.2.3",
56
- "esprima": "4.0.1",
57
- "mikro-orm": "7.0.0-dev.60",
55
+ "mikro-orm": "7.0.0-dev.62",
58
56
  "reflect-metadata": "0.2.2"
59
57
  },
60
58
  "peerDependencies": {
package/typings.d.ts CHANGED
@@ -451,9 +451,8 @@ export interface EntityMetadata<T = any> {
451
451
  discriminatorValue?: number | string;
452
452
  discriminatorMap?: Dictionary<string>;
453
453
  embeddable: boolean;
454
- constructorParams: EntityKey<T>[];
454
+ constructorParams?: (keyof T)[];
455
455
  forceConstructor: boolean;
456
- toJsonParams: string[];
457
456
  extends: string;
458
457
  collection: string;
459
458
  path: string;
@@ -25,7 +25,6 @@ export declare class ConfigurationLoader {
25
25
  * This method is used only in CLI context.
26
26
  */
27
27
  static registerTypeScriptSupport(configPath?: string, tsLoader?: 'swc' | 'tsx' | 'jiti' | 'tsimp' | 'auto'): Promise<boolean>;
28
- static registerDotenv<D extends IDatabaseDriver>(options: Options<D>): void;
29
28
  static loadEnvironmentVars<D extends IDatabaseDriver>(): Promise<Partial<Options<D>>>;
30
29
  static loadEnvironmentVarsSync<D extends IDatabaseDriver>(): Partial<Options<D>>;
31
30
  static getORMPackages(): Set<string>;
@@ -1,4 +1,3 @@
1
- import dotenv from 'dotenv';
2
1
  import { realpathSync } from 'node:fs';
3
2
  import { colors } from '../logging/colors.js';
4
3
  import { Configuration } from './Configuration.js';
@@ -185,17 +184,6 @@ export class ConfigurationLoader {
185
184
  console.warn('Neither `swc`, `tsx`, `jiti` nor `tsimp` found in the project dependencies, support for working with TypeScript files might not work. To use `swc`, you need to install both `@swc-node/register` and `@swc/core`.');
186
185
  return false;
187
186
  }
188
- static registerDotenv(options) {
189
- const path = process.env.MIKRO_ORM_ENV ?? ((options.baseDir ?? process.cwd()) + '/.env');
190
- const env = {};
191
- dotenv.config({ path, processEnv: env, quiet: true });
192
- // only propagate known env vars
193
- for (const key of Object.keys(env)) {
194
- if (key.startsWith('MIKRO_ORM_')) {
195
- process.env[key] ??= env[key]; // respect user provided values
196
- }
197
- }
198
- }
199
187
  static async loadEnvironmentVars() {
200
188
  const ret = this.loadEnvironmentVarsSync();
201
189
  // only to keep some sort of back compatibility with those using env vars only, to support `MIKRO_ORM_TYPE`
package/utils/Utils.d.ts CHANGED
@@ -2,7 +2,6 @@ import type { Dictionary, EntityData, EntityDictionary, EntityKey, EntityMetadat
2
2
  import type { Collection } from '../entity/Collection.js';
3
3
  import type { Platform } from '../platforms/Platform.js';
4
4
  import type { ScalarReference } from '../entity/Reference.js';
5
- export declare const ObjectBindingPattern: unique symbol;
6
5
  export declare function compareObjects(a: any, b: any): boolean;
7
6
  export declare function compareArrays(a: any[] | string, b: any[] | string): boolean;
8
7
  export declare function compareBooleans(a: unknown, b: unknown): boolean;
@@ -101,26 +100,11 @@ export declare class Utils {
101
100
  */
102
101
  static renameKey<T>(payload: T, from: string | keyof T, to: string): void;
103
102
  /**
104
- * Returns array of functions argument names. Uses `esprima` for source code analysis.
103
+ * Returns array of functions argument names. Uses basic regex for source code analysis, might not work with advanced syntax.
105
104
  */
106
- static tokenize(func: {
105
+ static getConstructorParams(func: {
107
106
  toString(): string;
108
- } | string | {
109
- type: string;
110
- value: string;
111
- }[]): {
112
- type: string;
113
- value: string;
114
- }[];
115
- /**
116
- * Returns array of functions argument names. Uses `esprima` for source code analysis.
117
- */
118
- static getParamNames(func: {
119
- toString(): string;
120
- } | string | {
121
- type: string;
122
- value: string;
123
- }[], methodName?: string): string[];
107
+ }): string[] | undefined;
124
108
  /**
125
109
  * Checks whether the argument looks like primary key (string, number or ObjectId).
126
110
  */
package/utils/Utils.js CHANGED
@@ -3,11 +3,9 @@ import { extname, isAbsolute, join, normalize, relative, resolve } from 'node:pa
3
3
  import { fileURLToPath, pathToFileURL } from 'node:url';
4
4
  import { existsSync, globSync, statSync, mkdirSync, readFileSync } from 'node:fs';
5
5
  import { createHash } from 'node:crypto';
6
- import { tokenize } from 'esprima';
7
6
  import { clone } from './clone.js';
8
7
  import { ARRAY_OPERATORS, GroupOperator, JSON_KEY_OPERATORS, PlainObject, QueryOperator, ReferenceKind, } from '../enums.js';
9
8
  import { helper } from '../entity/wrap.js';
10
- export const ObjectBindingPattern = Symbol('ObjectBindingPattern');
11
9
  function compareConstructors(a, b) {
12
10
  if (a.constructor === b.constructor) {
13
11
  return true;
@@ -357,53 +355,37 @@ export class Utils {
357
355
  }
358
356
  }
359
357
  /**
360
- * Returns array of functions argument names. Uses `esprima` for source code analysis.
358
+ * Returns array of functions argument names. Uses basic regex for source code analysis, might not work with advanced syntax.
361
359
  */
362
- static tokenize(func) {
363
- if (Array.isArray(func)) {
364
- return func;
365
- }
366
- /* v8 ignore next 5 */
367
- try {
368
- return tokenize(func.toString(), { tolerant: true });
369
- }
370
- catch {
371
- return [];
372
- }
373
- }
374
- /**
375
- * Returns array of functions argument names. Uses `esprima` for source code analysis.
376
- */
377
- static getParamNames(func, methodName) {
378
- const ret = [];
379
- const tokens = this.tokenize(func);
380
- let inside = 0;
381
- let currentBlockStart = 0;
382
- for (let i = 0; i < tokens.length; i++) {
383
- const token = tokens[i];
384
- if (token.type === 'Identifier' && token.value === methodName) {
385
- inside = 1;
386
- currentBlockStart = i;
387
- continue;
360
+ static getConstructorParams(func) {
361
+ const source = func.toString();
362
+ const i = source.indexOf('constructor');
363
+ if (i === -1) {
364
+ return undefined;
365
+ }
366
+ const start = source.indexOf('(', i);
367
+ if (start === -1) {
368
+ return undefined;
369
+ }
370
+ let depth = 0;
371
+ let end = start;
372
+ for (; end < source.length; end++) {
373
+ if (source[end] === '(') {
374
+ depth++;
388
375
  }
389
- if (inside === 1 && token.type === 'Punctuator' && token.value === '(') {
390
- inside = 2;
391
- currentBlockStart = i;
392
- continue;
376
+ if (source[end] === ')') {
377
+ depth--;
393
378
  }
394
- if (inside === 2 && token.type === 'Punctuator' && token.value === ')') {
379
+ if (depth === 0) {
395
380
  break;
396
381
  }
397
- if (inside === 2 && token.type === 'Punctuator' && token.value === '{' && i === currentBlockStart + 1) {
398
- ret.push(ObjectBindingPattern);
399
- i = tokens.findIndex((t, idx) => idx > i + 2 && t.type === 'Punctuator' && t.value === '}');
400
- continue;
401
- }
402
- if (inside === 2 && token.type === 'Identifier') {
403
- ret.push(token.value);
404
- }
405
382
  }
406
- return ret;
383
+ const raw = source.slice(start + 1, end);
384
+ return raw
385
+ .split(',')
386
+ .map(s => s.trim().replace(/=.*$/, '').trim())
387
+ .filter(Boolean)
388
+ .map(raw => raw.startsWith('{') && raw.endsWith('}') ? '' : raw);
407
389
  }
408
390
  /**
409
391
  * Checks whether the argument looks like primary key (string, number or ObjectId).