mix-classes
Version:
Seamlessly combine class inheritance with composition, guaranteed to work with any class
222 lines (181 loc) • 6.58 kB
JavaScript
;
function _setPrototypeOf(o, p) {
_setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
o.__proto__ = p;
return o;
};
return _setPrototypeOf(o, p);
}
function isNativeReflectConstruct() {
if (typeof Reflect === "undefined" || !Reflect.construct) return false;
if (Reflect.construct.sham) return false;
if (typeof Proxy === "function") return true;
try {
Date.prototype.toString.call(Reflect.construct(Date, [], function () {}));
return true;
} catch (e) {
return false;
}
}
function _construct(Parent, args, Class) {
if (isNativeReflectConstruct()) {
_construct = Reflect.construct;
} else {
_construct = function _construct(Parent, args, Class) {
var a = [null];
a.push.apply(a, args);
var Constructor = Function.bind.apply(Parent, a);
var instance = new Constructor();
if (Class) _setPrototypeOf(instance, Class.prototype);
return instance;
};
}
return _construct.apply(null, arguments);
}
/**
* Gets a specific mixin's `this`
* @param instance The this object to search on
* @param MixinClass The mixin you want to find
*/
var getMixin = function getMixin(instance, MixinClass) {
if (instance && instance[INSTANCE_THIS]) {
return instance[INSTANCE_THIS].get(MixinClass);
}
return undefined;
};
var INSTANCE_THIS =
/*#__PURE__*/
Symbol('instanceThis');
var MIXIN_CLASSES =
/*#__PURE__*/
Symbol('mixinClasses');
var extend = function extend(base, extension) {
return new Proxy(base, {
get: function get(_, prop) {
var target = prop in extension ? extension : base;
return target[prop];
},
set: function set(_, prop, value) {
var target = prop in extension ? extension : base;
target[prop] = value;
return true;
}
});
};
var extractConstructable = function extractConstructable(Mixable) {
return 'prototype' in Mixable ? Mixable : Mixable.Class;
};
var createMixinClass = function createMixinClass(Mixables) {
var _a, _b, _c;
var Classes = Mixables.map(extractConstructable);
var MixinClass = (_c = function MixinClass() {
var _this = this;
for (var _len = arguments.length, classesArgs = new Array(_len), _key = 0; _key < _len; _key++) {
classesArgs[_key] = arguments[_key];
}
// Stores the `this` proxies for each class
this[_b] = new WeakMap();
Classes.forEach(function (Class, i) {
var instance = _construct(Class, classesArgs[i] || []);
var instanceThis = extend(_this, instance);
_this[INSTANCE_THIS].set(Class, instanceThis); // Copy over getters to instance values
Object.keys(instance).forEach(function (key) {
Object.defineProperty(_this, key, {
configurable: true,
enumerable: true,
get: function get() {
return instance[key];
},
set: function set(value) {
return instance[key] = value;
}
});
});
});
}, _a = MIXIN_CLASSES, _b = INSTANCE_THIS, _c[_a] = Classes, _c);
Classes.forEach(function (Class) {
var restoreThisInsideFunction = function restoreThisInsideFunction(fn) {
return function () {
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
args[_key2] = arguments[_key2];
}
return fn.apply(getMixin(this, Class), args);
};
}; // Copy over prototype methods
var recursePrototype = function recursePrototype(prototype) {
// Add instanceof support
var hasInstance = prototype.constructor[Symbol.hasInstance];
Object.defineProperty(prototype.constructor, Symbol.hasInstance, {
configurable: true,
value: function value(possibleMixin) {
// Retain original instanceof for prototype
if (prototype.isPrototypeOf(possibleMixin)) return true;
if (possibleMixin && possibleMixin.constructor) {
var isInMixins = function isInMixins(mixin) {
var classes = mixin[MIXIN_CLASSES];
if (!classes) return false;
for (var _iterator = classes, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) {
var _ref;
if (_isArray) {
if (_i >= _iterator.length) break;
_ref = _iterator[_i++];
} else {
_i = _iterator.next();
if (_i.done) break;
_ref = _i.value;
}
var cls = _ref;
if (cls === Class) return true;
var isChildMixin = isInMixins(cls);
if (isChildMixin) return true;
}
return false;
};
if (this && this !== prototype.constructor) {
// not used as mixin, `class [this] extends [prototype.constructor] {}`
return prototype.constructor.isPrototypeOf(possibleMixin.constructor);
}
if (isInMixins(possibleMixin.constructor)) return true;
if (!this) return false;
}
return hasInstance(possibleMixin);
}
});
Object.getOwnPropertyNames(prototype).forEach(function (name) {
if (name === 'constructor') return;
var descriptor = Object.getOwnPropertyDescriptor(prototype, name);
if (descriptor.get) {
descriptor.get = restoreThisInsideFunction(descriptor.get);
}
if (descriptor.set) {
descriptor.set = restoreThisInsideFunction(descriptor.set);
}
if (typeof descriptor.value === 'function') {
descriptor.value = restoreThisInsideFunction(descriptor.value);
}
if (!MixinClass.prototype.hasOwnProperty(name)) {
Object.defineProperty(MixinClass.prototype, name, descriptor);
}
});
var parent = Object.getPrototypeOf(prototype);
if (parent && parent !== Object.prototype) recursePrototype(parent);
};
recursePrototype(Class.prototype);
});
return MixinClass;
};
var Mix = function Mix() {
for (var _len = arguments.length, Mixables = new Array(_len), _key = 0; _key < _len; _key++) {
Mixables[_key] = arguments[_key];
}
return createMixinClass(Mixables);
};
var Generic = function Generic(Class) {
return {
Class: Class
};
};
exports.Generic = Generic;
exports.Mix = Mix;
exports.getMixin = getMixin;
//# sourceMappingURL=mix-classes.cjs.development.js.map