UNPKG

@opra/common

Version:
121 lines (120 loc) 4.03 kB
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; }