UNPKG

mixwith

Version:

A simple, powerful mixin applier for JavaScript classes

96 lines (82 loc) 3.03 kB
'use strict'; (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); } } });