UNPKG

@sugarcoated/fondant-module

Version:
163 lines (139 loc) 4.16 kB
/** * Declaration of the Module class. Module simplifies management of a class * prototype in Node-inspired way with exportations and extensions. * * @fileoverview Module. Modularised prototype management. * @author Joshua Crowe, Junior Web Developer. * @copyright Joshua Crowe, 2017. */ const Dictionary = require('@sugarcoated/fondant-dictionary'); /** * Manage the prototype of a class. * * @class Module. * @classdesc Modularised prototype management. */ class Module { /** * @property reference * @description Reference of the class. * @type {Class} */ get reference () { return this._moduleReference; } /** * @property exports * @description Exports of the class. * @type {Dictionary.<String, Function>} */ get exports () { return this._moduleExports; } /** * Create a new Module. * @constructor * * @name Module#construcotr * @example new Module(a) * @param {Module} [moduleReference] Reference to class. * @returns {Module} */ constructor (moduleReference = Module) { /** * @property _moduleReference * @description Reference to the module. * @type {Class} * @private */ this._moduleReference = moduleReference; /** * @property _moduleExports * @description Exportations from the class. * @type {Dictionary.<String, Function>} */ this._moduleExports = new Dictionary(Function); /** * @const modulePrototype * @description Property names on the prototype. * @type {Array.<String>} */ const modulePrototype = Object.getOwnPropertyNames(moduleReference.prototype); /** * @const moduleMethods * @description Property names of actual methods. * @type {Array.<String>} */ const moduleMethods = modulePrototype.filter(modulePrototypal => { return (moduleReference.prototype[modulePrototypal] instanceof Function); }); moduleMethods.forEach(moduleProto => { /** * @const moduleMethod * @description Module prototype method. * @type {Function} */ const moduleMethod = moduleReference.prototype[moduleProto]; this._moduleExports.set(moduleProto, moduleMethod); }); } /** * Export a method. * * @name Module#export * @example myModule.export(a, b) * @param {String} exportName Name of the method. * @param {Module} [exportBind] Binding for the method. * @returns {Function} */ export (exportName, exportBind) { /** * @const exportReference * @type {Class} */ const exportReference = this._moduleReference; /** * @const exportMethods * @type {Dictionary.<String, Function>} */ const exportMethods = this._moduleExports; if (exportMethods.has(exportName)) { /** * @const exportMethod * @type {Function} */ const exportMethod = exportMethods.get(exportName); return exportMethod.bind(exportBind || exportReference); } else { throw new TypeError(`'${exportName}' not on prototype.`); } } /** * Extend the module. * * @name Module#extend * @example myModule.extend(a, b, c) * @param {String} extendName Name of the method to add. * @param {Function} extendMethod Method to add. * @param {Module} [extendBind] Module to bind to the method. */ extend (extendName, extendMethod, extendBind) { /** * @const extendExports * @type {Dictionary.<String, Function>} */ const extendExports = this._moduleExports; if (!extendExports.has(extendName)) { /** * @const extendRebind * @type {Function} */ const extendRebind = extendMethod.bind(extendBind || this); this.__proto__[extendName] = extendRebind; extendExports.set(extendName, extendRebind); } else { throw new TypeError(`'${extendName}' already on prototype.`); } } } module.exports = Module;