@sugarcoated/fondant-module
Version:
163 lines (139 loc) • 4.16 kB
JavaScript
/**
* 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;