export function PropertyMixin(key, meta = {}) { if (!meta || 'object' !== typeof meta) { throw new TypeError( `Expected meta for Property(${ key }) to be object. ${ JSON.stringify(meta) } given.` ); } return (Superclass) => { if (Superclass.prototype[key]) { throw new TypeError(`can't redefine Avocado property "${key}"`); } if ('identity' === meta.transformProperty) { meta.transformProperty = (key) => key; } else if (!meta.transformProperty) { meta.transformProperty = (key) => `${key}_PRIVATE_PROPERTY`; } meta.emit = meta.emit || function(...args) { if (this.emit) { this.emit(...args); } } meta.eq = meta.eq || function (l, r) { return l === r; } meta.get = meta.get || function(value) { return this[meta.transformProperty(key)]; } meta.set = meta.set || function(value) { this[meta.transformProperty(key)] = value; } let metaDefault; if (null === meta.default) { metaDefault = null; } else if (undefined === meta.default) { metaDefault = undefined; } else { metaDefault = JSON.parse(JSON.stringify(meta.default)); } class Property extends Superclass { constructor(...args) { super(...args); if (undefined !== metaDefault) { meta.set.call(this, metaDefault); } } } Object.defineProperty(Property.prototype, key, { enumerable: true, get: function() { return meta.get.call(this); }, set: function (value) { const old = meta.get.call(this); meta.set.call(this, value); if (meta.track && meta.emit && !meta.eq.call(this, old, value)) { meta.emit.call(this, `${key}Changed`, old, value); } }, }); return Property; } }