@prostojs/mate
Version:
MATE is TS Metadata Organizer
258 lines (254 loc) • 10.3 kB
JavaScript
function getConstructor(instance) {
return isConstructor(instance) ?
instance : instance.constructor ?
instance.constructor : Object.getPrototypeOf(instance).constructor;
}
function isConstructor(v) {
return typeof v === 'function' && Object.getOwnPropertyNames(v).includes('prototype') && !Object.getOwnPropertyNames(v).includes('caller') && !!v.name;
}
let classMetadata = new WeakMap();
let paramMetadata = new WeakMap();
const root = typeof global === 'object' ? global : typeof self === 'object' ? self : {};
function getMetaObject(target, prop) {
const isParam = typeof prop !== 'undefined';
const metadata = isParam ? paramMetadata : classMetadata;
const targetKey = getConstructor(target);
let meta = metadata.get(targetKey);
if (!meta) {
meta = {};
metadata.set(targetKey, meta);
}
if (isParam) {
meta = (meta[prop] =
meta[prop] || {});
}
return meta;
}
const _reflect = {
getOwnMetadata(key, target, prop) {
return getMetaObject(target, prop)[key];
},
defineMetadata(key, data, target, prop) {
const meta = getMetaObject(target, prop);
meta[key] = data;
},
metadata(key, data) {
return ((target, propKey) => {
Reflect$1.defineMetadata(key, data, target, propKey);
});
},
_cleanup: (() => {
classMetadata = new WeakMap();
paramMetadata = new WeakMap();
}),
};
if (!root.Reflect) {
root.Reflect = _reflect;
}
else {
const funcs = [
'getOwnMetadata',
'defineMetadata',
'metadata',
];
const target = root.Reflect;
for (const func of funcs) {
if (typeof target[func] !== 'function') {
Object.defineProperty(target, func, { configurable: true, writable: true, value: _reflect[func] });
}
}
}
const Reflect$1 = _reflect;
const Reflect = ((global === null || global === void 0 ? void 0 : global.Reflect) ||
(self === null || self === void 0 ? void 0 : self.Reflect) ||
Reflect$1);
class Mate {
constructor(workspace, options = {}) {
this.workspace = workspace;
this.options = options;
this.logger = options.logger || console;
}
_cleanup() {
var _a;
(_a = Reflect._cleanup) === null || _a === void 0 ? void 0 : _a.call(Reflect);
}
set(args, key, value, isArray) {
var _a;
let level = 'CLASS';
const newArgs = args.level === 'CLASS'
? { target: args.target }
: args.level === 'PROP'
? { target: args.target, propKey: args.propKey }
: args;
let meta = (Reflect.getOwnMetadata(this.workspace, newArgs.target, newArgs.propKey) || {});
if (newArgs.propKey &&
this.options.readReturnType &&
!meta.returnType &&
args.descriptor) {
meta.returnType = Reflect.getOwnMetadata('design:returntype', newArgs.target, newArgs.propKey);
}
if (newArgs.propKey && this.options.readType && !meta.type) {
meta.type = Reflect.getOwnMetadata('design:type', newArgs.target, newArgs.propKey);
}
const { index } = newArgs;
const cb = typeof key === 'function' ? key : undefined;
let data = meta;
if (!data.params) {
data.params = (_a = Reflect.getOwnMetadata('design:paramtypes', newArgs.target, newArgs.propKey)) === null || _a === void 0 ? void 0 : _a.map((f) => ({ type: f }));
}
if (typeof index === 'number') {
level = 'PARAM';
data.params = data.params || [];
data.params[index] = data.params[index] || {
type: undefined,
};
if (cb) {
data.params[index] = cb(data.params[index], level, args.propKey, typeof args.index === 'number' ? args.index : undefined);
}
else {
data = data.params[index];
}
}
else if (!index &&
!args.descriptor &&
args.propKey &&
this.options.collectPropKeys &&
args.level !== 'CLASS') {
this.set({ ...args, level: 'CLASS' }, (meta) => {
if (!meta.properties) {
meta.properties = [args.propKey];
}
else if (!meta.properties.includes(args.propKey)) {
meta.properties.push(args.propKey);
}
return meta;
});
}
level =
typeof index === 'number'
? 'PARAM'
: newArgs.propKey && newArgs.descriptor
? 'METHOD'
: newArgs.propKey
? 'PROP'
: 'CLASS';
if (typeof key !== 'function') {
if (isArray) {
const newArray = (data[key] || []);
if (!Array.isArray(newArray)) {
this.logger.error('Mate.add (isArray=true) called for non-array metadata');
}
newArray.unshift(value);
data[key] = newArray;
}
else {
data[key] = value;
}
}
else if (cb && typeof index !== 'number') {
meta = cb(data, level, args.propKey, typeof args.index === 'number' ? args.index : undefined);
}
Reflect.defineMetadata(this.workspace, meta, newArgs.target, newArgs.propKey);
}
read(target, propKey) {
var _a;
const isConstr = isConstructor(target);
const constructor = isConstr ? target : getConstructor(target);
const proto = constructor.prototype;
let ownMeta = Reflect.getOwnMetadata(this.workspace, typeof propKey === 'string' ? proto : constructor, propKey);
if (ownMeta && propKey === undefined && ownMeta.params === undefined) {
const parent = Object.getPrototypeOf(constructor);
if (typeof parent === 'function' &&
parent !== fnProto &&
parent !== constructor) {
ownMeta.params = (_a = this.read(parent)) === null || _a === void 0 ? void 0 : _a.params;
}
}
if (this.options.inherit) {
const inheritFn = typeof this.options.inherit === 'function'
? this.options.inherit
: undefined;
let shouldInherit = this.options.inherit;
if (inheritFn) {
if (typeof propKey === 'string') {
const classMeta = Reflect.getOwnMetadata(this.workspace, constructor);
shouldInherit = inheritFn(classMeta, ownMeta, 'PROP', propKey);
}
else {
shouldInherit = inheritFn(ownMeta, ownMeta, 'CLASS');
}
}
if (shouldInherit) {
const parent = Object.getPrototypeOf(constructor);
if (typeof parent === 'function' &&
parent !== fnProto &&
parent !== constructor) {
const inheritedMeta = (this.read(parent, propKey) ||
{});
const ownParams = ownMeta === null || ownMeta === void 0 ? void 0 : ownMeta.params;
ownMeta = { ...inheritedMeta, ...ownMeta };
if (typeof propKey === 'string' &&
ownParams &&
(inheritedMeta === null || inheritedMeta === void 0 ? void 0 : inheritedMeta.params)) {
for (let i = 0; i < ownParams.length; i++) {
if (typeof (inheritedMeta === null || inheritedMeta === void 0 ? void 0 : inheritedMeta.params[i]) !== 'undefined') {
const ownParam = ownParams[i];
if (ownMeta.params &&
inheritFn &&
inheritFn(ownMeta, ownParam, 'PARAM', typeof propKey === 'string'
? propKey
: undefined)) {
ownMeta.params[i] = {
...inheritedMeta === null || inheritedMeta === void 0 ? void 0 : inheritedMeta.params[i],
...ownParams[i],
};
}
}
}
}
}
}
}
return ownMeta;
}
apply(...decorators) {
return ((target, propKey, descriptor) => {
for (const d of decorators) {
d(target, propKey, descriptor);
}
});
}
decorate(key, value, isArray, level) {
return ((target, propKey, descriptor) => {
const args = {
target,
propKey,
descriptor: typeof descriptor === 'number' ? undefined : descriptor,
index: typeof descriptor === 'number' ? descriptor : undefined,
level,
};
this.set(args, key, value, isArray);
});
}
decorateConditional(ccb) {
return ((target, propKey, descriptor) => {
const hasIndex = typeof descriptor === 'number';
const decoratorLevel = hasIndex
? 'PARAM'
: propKey && descriptor
? 'METHOD'
: propKey
? 'PROP'
: 'CLASS';
const d = ccb(decoratorLevel);
if (d) {
d(target, propKey, descriptor);
}
});
}
decorateClass(key, value, isArray) {
return this.decorate(key, value, isArray, 'CLASS');
}
}
const fnProto = Object.getPrototypeOf(Function);
export { Mate, getConstructor, isConstructor };