@type-r/models
Version:
The serializable type system for JS and TypeScript
103 lines (80 loc) • 4.28 kB
text/typescript
import { define, predefine, tools } from '@type-r/mixture';
import { Transactional } from '../transactions';
import { type } from './attrDef';
import { addAttributeLinks } from './linked-attrs';
import { createAttributesMixin } from './mixin';
import { Model, ModelConstructor, ModelDefinition } from './model';
export * from './attrDef';
export * from './metatypes';
export { AttributesMixin, InferAttrs, LinkedAttributes, ModelConstructor } from './model';
export { Model };
const { assign, defaults } = tools;
export function attributes<D extends object, B1 extends typeof Model, B2 extends typeof Model, B3 extends typeof Model>( b1 : B1, b2 : B2, b3 : B3, attrDefs : D ) : ModelConstructor<D & B1['attributes'] & B2['attributes'] & B3['attributes']>;
export function attributes<D extends object, B1 extends typeof Model, B2 extends typeof Model>( b1 : B1, b2 : B2, attrDefs : D ) : ModelConstructor<D & B1['attributes'] & B2['attributes']>;
export function attributes<D extends object, B1 extends typeof Model>( b1 : B1, attrDefs : D ) : ModelConstructor<D & B1['attributes']>;
export function attributes<D extends object>( attrDefs : D ) : ModelConstructor<D>;
export function attributes<D extends object>( ...models : any ) : ModelConstructor<D> {
const attrs = models.map( x => x instanceof Function ? x.attributes : x ),
attrDefs = assign( {}, ...attrs );
class NamelessModel extends Model {
static attributes = attrDefs;
}
return NamelessModel as any;
}
Model.onExtend = function( this : typeof Model, BaseClass : typeof Model ){
Transactional.onExtend.call( this, BaseClass );
// Create the default collection
const Class = this;
class DefaultCollection extends BaseClass.Collection {
static model = Class;
}
this.DefaultCollection = DefaultCollection;
// If there are no collection defined in statics, use the default collection.
// It will appear in onDefine's definition, overriding all other settings.
if( Class.Collection === BaseClass.Collection ){
this.Collection = DefaultCollection;
}
}
Model.onDefine = function( definition : ModelDefinition, BaseClass : typeof Model ){
const baseProto : Model = BaseClass.prototype;
// Compile attributes spec, creating definition mixin.
const { properties, _localEvents, ...dynamicMixin } = createAttributesMixin( this.attributes = getAttributes( definition ), baseProto._attributes );
assign( this.prototype, dynamicMixin );
definition.properties = defaults( definition.properties || {}, properties );
definition._localEvents = _localEvents;
Transactional.onDefine.call( this, definition, BaseClass );
// Finalize the definition of the default collection.
this.DefaultCollection.define( definition.collection || {} );
// assign collection from the definition.
this.Collection = definition.Collection;
this.Collection.prototype.model = this;
if( definition.endpoint ) this.Collection.prototype._endpoint = definition.endpoint;
addAttributeLinks( this );
}
function getAttributes({ defaults, attributes, idAttribute } : ModelDefinition ) {
const definition = attributes || defaults || {};
// If there is an undeclared idAttribute, add its definition as untyped generic attribute.
if( idAttribute && !( idAttribute in definition ) ){
definition[ idAttribute ] = void 0;
}
return definition;
}
declare var Reflect;
export function auto( value : any ) : PropertyDecorator;
export function auto( proto : object, attrName : string ) : void;
export function auto( proto, attrName? : string ) : any {
if( typeof Reflect !== 'undefined' && Reflect.getMetadata ){
if( attrName ){
type( Reflect.getMetadata( "design:type", proto, attrName ) ).as( proto, attrName );
}
else{
const value = proto;
return ( proto : object, attrName : string ) : void => {
type( Reflect.getMetadata( "design:type", proto, attrName ) ).value( value ).as( proto, attrName );
}
}
}
else{
proto._log( 'error', 'Type-R:MissingImport', 'Add import "reflect-metadata"; as the first line of your app.' );
}
}