@opra/common
Version:
Opra common package
121 lines (120 loc) • 4.03 kB
JavaScript
import 'reflect-metadata';
import { omitUndefined } from '@jsopen/objects';
import { asMutable } from 'ts-gems';
import { inheritPropertyInitializers, mergePrototype, } from '../../helpers/index.js';
import { OpraSchema } from '../../schema/index.js';
import { DATATYPE_METADATA, DECORATOR } from '../constants.js';
import { ApiField } from './api-field.js';
import { ComplexTypeBase } from './complex-type-base.js';
import { DataType } from './data-type.js';
/**
* @class MixinType
*/
export const MixinType = function (...args) {
// MixinType factory
if (!this) {
return MixinType[DECORATOR].apply(undefined, args);
}
// Constructor
const [owner, initArgs, context] = args;
ComplexTypeBase.call(this, owner, initArgs, context);
const _this = asMutable(this);
_this.kind = OpraSchema.MixinType.Kind;
_this.types = [];
for (const base of initArgs.types) {
if (_this.additionalFields !== true) {
if (base.additionalFields === true)
_this.additionalFields = true;
else if (!_this.additionalFields)
_this.additionalFields = base.additionalFields;
}
for (const v of base.fields('*')) {
const field = new ApiField(this, v);
_this._fields.set(field.name, field);
}
_this.types.push(base);
if (base.keyField)
_this.keyField = base.keyField;
}
};
/**
*
* @class MixinType
*/
class MixinTypeClass extends ComplexTypeBase {
extendsFrom(baseType) {
if (!(baseType instanceof DataType))
baseType = this.node.getDataType(baseType);
if (!(baseType instanceof ComplexTypeBase))
return false;
if (baseType === this)
return true;
for (const t of this.types) {
if (t.extendsFrom(baseType))
return true;
}
return false;
}
toJSON(options) {
const superJson = super.toJSON(options);
return omitUndefined({
...superJson,
kind: this.kind,
types: this.types.map(base => {
const baseName = this.node.getDataTypeNameWithNs(base);
return baseName ? baseName : base.toJSON(options);
}),
});
}
_locateBase(callback) {
for (const t of this.types) {
if (callback(t))
return t;
if (t._locateBase) {
const x = t._locateBase(callback);
if (x)
return x;
}
}
}
}
MixinType.prototype = MixinTypeClass.prototype;
MixinType[DECORATOR] = MixinTypeFactory;
/**
*
*/
function MixinTypeFactory(clasRefs, options) {
// Filter undefined items
clasRefs = clasRefs.filter(x => typeof x === 'function');
if (!clasRefs.length)
throw new TypeError('No Class has been provided');
if (clasRefs.length === 1)
return clasRefs[0];
const className = clasRefs[0].name + 'Mixin';
const MixinClass = {
[className]: class {
constructor() {
for (const c of clasRefs)
inheritPropertyInitializers(this, c);
}
},
}[className];
const metadata = {
...options,
kind: OpraSchema.MixinType.Kind,
types: [],
};
Reflect.defineMetadata(DATATYPE_METADATA, metadata, MixinClass);
for (const c of clasRefs) {
const itemMeta = Reflect.getMetadata(DATATYPE_METADATA, c);
if (!(itemMeta &&
(itemMeta.kind === OpraSchema.ComplexType.Kind ||
itemMeta.kind === OpraSchema.MixinType.Kind ||
itemMeta.kind === OpraSchema.MappedType.Kind))) {
throw new TypeError(`Class "${c.name}" is not a ${OpraSchema.ComplexType.Kind}, ${OpraSchema.MixinType.Kind} or ${OpraSchema.MappedType.Kind}`);
}
metadata.types.push(c);
mergePrototype(MixinClass.prototype, c.prototype);
}
return MixinClass;
}