@essential-projects/metadata
Version:
the core metadata service for using the metadata from inside the domain
165 lines (112 loc) • 4.71 kB
text/typescript
import {IMetadataProvider, MetadataType} from '@essential-projects/metadata_contracts';
import {Logger} from 'loggerhythm';
import * as merge from 'deepmerge';
import 'reflect-metadata';
const logger: Logger = new Logger('MetadataProvider');
const GLOBAL_METADATA_STORE_KEY: string = '$metadata';
declare var global: any;
declare var window: any;
let globalVariable: any;
if (typeof global !== 'undefined') {
globalVariable = global;
} else {
globalVariable = window;
}
function getGlobalMetadataStore(): any {
if (!globalVariable[GLOBAL_METADATA_STORE_KEY]) {
globalVariable[GLOBAL_METADATA_STORE_KEY] = {};
}
return globalVariable[GLOBAL_METADATA_STORE_KEY];
}
if (typeof globalVariable.existingMetadataProvider === 'undefined') {
globalVariable.existingMetadataProvider = {
getForInstance(metadataKey: MetadataType, namespace: string, target: any, targetKey?: string): any {
if (!isObject(target)) {
return undefined;
}
let result: any = MetadataProvider.getForOwnInstance(metadataKey, namespace, target, targetKey);
if (result === undefined) {
result = MetadataProvider.getForInstance(metadataKey, Object.getPrototypeOf(target), targetKey);
}
return result;
},
getForOwnInstance(metadataKey: MetadataType, namespace: string, target: any, targetKey?: string): any {
if (!isObject(target)) {
return undefined;
}
const type: string = target.constructor.name;
let metadataForType: any = {};
// fetching the metadata for Object would result in a stack overflow
if (type !== 'Object') {
metadataForType = MetadataProvider.getForType(metadataKey, namespace, type, targetKey);
}
const ownMetadata: any = Reflect.getOwnMetadata(metadataKey, target, targetKey);
let result: any;
if (metadataForType === undefined) {
result = ownMetadata;
} else if (ownMetadata === undefined) {
result = metadataForType;
} else {
result = merge(metadataForType, ownMetadata);
}
return result;
},
getForType(metadataKey: MetadataType, namespace: string, type: string, targetKey?: string): any {
const allTypeMetadata: any = getGlobalMetadataStore();
const namespaceMetadata: any = allTypeMetadata[namespace];
if (!namespaceMetadata) {
return undefined;
}
const typeMetadata: any = namespaceMetadata[type];
if (!typeMetadata) {
return undefined;
}
const typeMetadataForKey: any = typeMetadata[metadataKey];
if (!typeMetadataForKey) {
return undefined;
}
const typeMetadataForProperty: any = typeMetadataForKey[targetKey];
return typeMetadataForProperty;
},
getAllForType(metadataKey: MetadataType, namespace: string, type: string): any {
const allTypeMetadata: any = getGlobalMetadataStore();
const namespaceMetadata: any = allTypeMetadata[namespace];
if (!namespaceMetadata) {
return undefined;
}
const typeMetadata: any = namespaceMetadata[type];
if (!typeMetadata) {
return undefined;
}
const typeMetadataForKey: any = typeMetadata[metadataKey];
return typeMetadataForKey;
},
setForInstance(metadataKey: MetadataType, metadataValue: any, target: any, targetKey?: string): void {
Reflect.defineMetadata(metadataKey, metadataValue, target, targetKey);
},
hasType(namespace: string, type: string): boolean {
const allTypeMetadata: any = getGlobalMetadataStore();
return allTypeMetadata[namespace][type] !== undefined;
},
setForType(metadataKey: MetadataType, metadataValue: any, namespace: string, type: string, targetKey?: string): void {
const allTypeMetadata: any = getGlobalMetadataStore();
if (!allTypeMetadata[namespace]) {
allTypeMetadata[namespace] = {};
}
if (!allTypeMetadata[namespace][type]) {
allTypeMetadata[namespace][type] = {};
}
if (!allTypeMetadata[namespace][type][metadataKey]) {
allTypeMetadata[namespace][type][metadataKey] = {};
}
if (metadataValue && !allTypeMetadata[namespace][type][metadataKey][targetKey]) {
allTypeMetadata[namespace][type][metadataKey][targetKey] = {};
}
allTypeMetadata[namespace][type][metadataKey][targetKey] = merge(metadataValue, allTypeMetadata[namespace][type][metadataKey][targetKey]);
},
};
}
function isObject(value: any): boolean {
return value && (typeof value === 'function' || typeof value === 'object');
}
export const MetadataProvider: IMetadataProvider = globalVariable.existingMetadataProvider;