@mikro-orm/core
Version:
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.
112 lines (111 loc) • 4.37 kB
JavaScript
import { Utils } from '../utils/Utils.js';
import { EntitySchema } from './EntitySchema.js';
/** Base metadata provider that resolves entity type information and manages metadata caching. */
export class MetadataProvider {
config;
constructor(config) {
this.config = config;
}
/** Resolves entity references and type information for all properties in the given metadata. */
loadEntityMetadata(meta) {
for (const prop of meta.props) {
/* v8 ignore next */
if (typeof prop.entity === 'string') {
prop.type = prop.entity;
}
else if (prop.entity) {
const tmp = prop.entity();
prop.type = Array.isArray(tmp)
? tmp
.map(t => this.resolveEntityName(t))
.sort()
.join(' | ')
: this.resolveEntityName(tmp);
prop.target = EntitySchema.is(tmp) ? tmp.meta.class : tmp;
}
else if (!prop.type && !((prop.enum || prop.array) && (prop.items?.length ?? 0) > 0)) {
throw new Error(`Please provide either 'type' or 'entity' attribute in ${meta.className}.${prop.name}.`);
}
}
}
/**
* Resolves the entity name for a given class or schema, respecting explicit names
* set via `defineEntity({ name })` + `setClass()`.
*/
resolveEntityName(entity) {
if (EntitySchema.is(entity)) {
return entity.meta.className;
}
const schema = EntitySchema.REGISTRY.get(entity);
if (schema) {
return schema.name;
}
return Utils.className(entity);
}
/** Merges cached metadata into the given entity metadata, preserving function expressions. */
loadFromCache(meta, cache) {
Object.values(cache.properties).forEach(prop => {
const metaProp = meta.properties[prop.name];
/* v8 ignore next */
if (metaProp?.enum && Array.isArray(metaProp.items)) {
delete prop.items;
}
});
// Preserve function expressions from indexes/uniques — they can't survive JSON cache serialization
const expressionMap = new Map();
for (const arr of [meta.indexes, meta.uniques]) {
for (const idx of arr ?? []) {
if (typeof idx.expression === 'function' && idx.name) {
expressionMap.set(idx.name, idx.expression);
}
}
}
Utils.mergeConfig(meta, cache);
// Restore function expressions that were lost during JSON serialization
if (expressionMap.size > 0) {
for (const arr of [meta.indexes, meta.uniques]) {
for (const idx of arr ?? []) {
const fn = idx.name && expressionMap.get(idx.name);
if (fn && typeof idx.expression !== 'function') {
idx.expression = fn;
}
}
}
}
}
/** Whether this provider class uses metadata caching by default. */
static useCache() {
return false;
}
/** Whether metadata caching is enabled for this instance. */
useCache() {
return this.config.get('metadataCache').enabled ?? MetadataProvider.useCache();
}
saveToCache(meta) {
//
}
/** Attempts to load metadata from cache, returning undefined if not available. */
getCachedMetadata(meta, root) {
if (!this.useCache()) {
return undefined;
}
const cache = meta.path && this.config.getMetadataCacheAdapter().get(this.getCacheKey(meta));
if (cache) {
this.loadFromCache(meta, cache);
meta.root = root;
}
return cache;
}
/** Combines individual metadata cache entries into a single file. */
combineCache() {
const path = this.config.getMetadataCacheAdapter().combine?.();
// override the path in the options, so we can log it from the CLI in `cache:generate` command
if (path) {
this.config.get('metadataCache').combined = path;
}
}
/** Returns the cache key for the given entity metadata. */
getCacheKey(meta) {
return meta.className;
}
}