mixwith
Version:
A simple, powerful mixin applier for JavaScript classes
96 lines (82 loc) • 3.03 kB
JavaScript
;
(function (global, factory) {
if (typeof define === "function" && define.amd) {
define(['exports'], factory);
} else if (typeof exports !== "undefined") {
factory(exports);
} else {
var mod = {
exports: {}
};
factory(mod.exports);
global.mixwith = mod.exports;
}
})(this, function (exports) {
Object.defineProperty(exports, "__esModule", {
value: true
});
const _cachedApplicationRef = exports._cachedApplicationRef = Symbol('_cachedApplicationRef');
const _mixinRef = exports._mixinRef = Symbol('_mixinRef');
const _originalMixin = exports._originalMixin = Symbol('_originalMixin');
const wrap = exports.wrap = (mixin, wrapper) => {
Object.setPrototypeOf(wrapper, mixin);
if (!mixin[_originalMixin]) {
mixin[_originalMixin] = mixin;
}
return wrapper;
};
const Cached = exports.Cached = mixin => wrap(mixin, superclass => {
// Get or create a symbol used to look up a previous application of mixin
// to the class. This symbol is unique per mixin definition, so a class will have N
// applicationRefs if it has had N mixins applied to it. A mixin will have
// exactly one _cachedApplicationRef used to store its applications.
let applicationRef = mixin[_cachedApplicationRef];
if (!applicationRef) {
applicationRef = mixin[_cachedApplicationRef] = Symbol(mixin.name);
}
// Look up an existing application of `mixin` to `c`, return it if found.
if (superclass.hasOwnProperty(applicationRef)) {
return superclass[applicationRef];
}
// Apply the mixin
let application = mixin(superclass);
// Cache the mixin application on the superclass
superclass[applicationRef] = application;
return application;
});
const HasInstance = exports.HasInstance = mixin => {
if (Symbol.hasInstance && !mixin.hasOwnProperty(Symbol.hasInstance)) {
Object.defineProperty(mixin, Symbol.hasInstance, {
value: function (o) {
const originalMixin = this[_originalMixin];
while (o != null) {
if (o.hasOwnProperty(_mixinRef) && o[_mixinRef] === originalMixin) {
return true;
}
o = Object.getPrototypeOf(o);
}
return false;
}
});
}
return mixin;
};
const BareMixin = exports.BareMixin = mixin => wrap(mixin, superclass => {
// Apply the mixin
let application = mixin(superclass);
// Attach a reference from mixin applition to wrapped mixin for RTTI
// mixin[@@hasInstance] should use this.
application.prototype[_mixinRef] = mixin[_originalMixin];
return application;
});
const Mixin = exports.Mixin = mixin => Cached(HasInstance(BareMixin(mixin)));
const mix = exports.mix = superClass => new MixinBuilder(superClass);
class MixinBuilder {
constructor(superclass) {
this.superclass = superclass;
}
with() {
return Array.from(arguments).reduce((c, m) => m(c), this.superclass);
}
}
});