UNPKG

node-warp

Version:

Web-agnostic Rapid Prototyping tools

362 lines (302 loc) 11.4 kB
'use strict'; var _createClass = require('babel-runtime/helpers/create-class')['default']; var _classCallCheck = require('babel-runtime/helpers/class-call-check')['default']; var _defineProperty = require('babel-runtime/helpers/define-property')['default']; var _slicedToArray = require('babel-runtime/helpers/sliced-to-array')['default']; var _toConsumableArray = require('babel-runtime/helpers/to-consumable-array')['default']; var _Object$assign = require('babel-runtime/core-js/object/assign')['default']; var _Map = require('babel-runtime/core-js/map')['default']; var _Object$entries = require('babel-runtime/core-js/object/entries')['default']; var _Promise = require('babel-runtime/core-js/promise')['default']; var _interopRequireDefault = require('babel-runtime/helpers/interop-require-default')['default']; Object.defineProperty(exports, '__esModule', { value: true }); var _debug = require('debug'); var _debug2 = _interopRequireDefault(_debug); var _path2 = require('path'); var _assert = require('assert'); var _assert2 = _interopRequireDefault(_assert); var _util = require('util'); var _bus = require('./bus'); var _bus2 = _interopRequireDefault(_bus); var Functors = { reduce: function reduce(prev, cur) { return _Object$assign(prev, cur); }, priorityAscending: function priorityAscending(a, b) { return a.config.priority - b.config.priority; }, priorityDescending: function priorityDescending(a, b) { return b.config.priority - a.config.priority; } }; /** * @desc Represents generic application skeleton, * providing api for simple state and component management */ var Application = (function () { _createClass(Application, null, [{ key: 'create', /** * Returns application instance * @param {Object} config * @param {String} config.name * @return {Application} */ value: function create(config) { return new Application(config); } }, { key: 'defaults', value: { name: 'app', componentsRoot: '.' }, /** * @param {Object} config * @param {String} config.name * @constructor */ enumerable: true }]); function Application(config) { var _this = this; _classCallCheck(this, Application); (0, _assert2['default'])(config.name, 'application name should be a string'); this.bus = new _bus2['default'](); this._components = new _Map(); this.name = config.name; this.config = _Object$assign({}, Application.defaults, config); this.data = _Object$assign({ name: this.name }, config.data); this.debug = (0, _debug2['default'])(this.name); this.bus.onRequest('app.destroy', function* _destroy(data) { this.debug('destroying application. reason: ' + data.reason); return yield this.destroy(); }, this); this.bus.on('app.data', function (data) { _this.data = _Object$assign({}, _this.data, data); }); this.bus.onRequest('app.query.data', function () { return _Object$assign({}, _this.data); }); this._setStatus('created'); } /** * @desc Starts an application: * calls 'init' method on each registered component, then fires 'app.started' event * @return {Promise} */ _createClass(Application, [{ key: 'start', value: function start() { var _this2 = this; this.debug('starting'); this._instantiateComponents(); return this._initializeComponents().then(function () { return _this2._setStatus('starting'); }).then(function () { return _this2._forEachComponent('onStart', Functors.priorityAscending); }).then(function () { return _this2._setStatus('started', _this2.data); }).then(function () { return _this2._forEachComponent('onAfterStart', Functors.priorityAscending); }); } /** * @desc Destroys an application: * calls 'destroy' method on each registered component, then fires 'app.destroyed' event * @return {Promise} */ }, { key: 'destroy', value: function destroy() { var _this3 = this; this.debug('destroying ' + this.name); return this._forEachComponent('onBeforeDestroy', Functors.priorityDescending).then(function () { return _this3._setStatus('destroying'); }).then(function () { return _this3._forEachComponent('onDestroy', Functors.priorityDescending); }).then(function () { return _this3._setStatus('destroyed'); }); } /** * @desc Registers component, represented by given class or module path * @param {Component|string} component * @param {object} [componentConfig] * @return {Promise} */ }, { key: 'use', value: function use(component) { var componentConfig = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; if ((0, _util.isString)(component)) { return this.useModule(component, componentConfig); } return this._addComponent(component, componentConfig); } }, { key: 'useModule', value: function useModule(path) { var config = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; this.debug('loading component module: ' + path); var component = require(path); return this._addComponent(component, config); } /** * @desc Loads whole bunch of components using configuration object. * @param {object<string,object>} componentsConfig * * @example configuration object: * let components = [{ * 'component-one': { * imports: [], * config: { someOption: 'someValue' } * }, * * 'component-two': { * imports: ['component-one'], // dependency declaration (it overrides the defaults) * config: { anotherOption: 'anotherValue' } * }, * * 'one-more-component': { * path: './path/to/my-components', // explicit path declaration (overrides 'config.app.componentsRoot') * imports: ['component-one', 'component-two'], * config: {} * }, * ... etc ... * }]; * * let warp = require('@hp/warp'); * let app = warp({ name: 'this-is-warp' }); * * // loads stuff synchronously * app.useConfig(components); * * // app is ready to start * app.start().then( ... ).catch( ... ); */ }, { key: 'useConfig', value: function useConfig() { var _this4 = this; var componentsConfig = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; _Object$entries(componentsConfig).forEach(function (_ref3) { var _ref32 = _slicedToArray(_ref3, 2); var key = _ref32[0]; var entry = _ref32[1]; var _path = entry.path || (0, _path2.resolve)(_this4.config.componentsRoot, key); var _component = _this4.useModule(_path, entry.config); _component.imports = entry.imports || _component.ComponentClass.imports || []; }); } }, { key: '_addComponent', value: function _addComponent(ComponentClass, config) { var id = config.id || ComponentClass.id || ComponentClass.name; (0, _assert2['default'])(!this._components.has(id), 'component with id \'' + id + '\' already registered'); var _entry = _Object$assign({}, { id: id, ComponentClass: ComponentClass, config: config }); this._components.set(id, _entry); this.debug('component added: ' + JSON.stringify({ id: id, name: ComponentClass.name })); return _entry; } }, { key: '_instantiateComponents', value: function _instantiateComponents() { var _this5 = this; var _entries = [].concat(_toConsumableArray(this._components.keys())).map(function (id) { return [id, _this5._createComponentInstance(id)]; }); this._components = new _Map(_entries); } }, { key: '_createComponentInstance', value: function _createComponentInstance(id) { this.debug('creating instance of [' + id + ']'); var entry = this._components.get(id); var instance = new entry.ComponentClass(id, this.bus); return _Object$assign({}, entry, { instance: instance }); } }, { key: '_getImportsFor', value: function _getImportsFor(_ref4) { var id = _ref4.id; var ComponentClass = _ref4.ComponentClass; var _ref4$imports = _ref4.imports; var imports = _ref4$imports === undefined ? ComponentClass.imports || [] : _ref4$imports; return (function () { var _this6 = this; this.debug('getting imports of ' + id + ': [' + imports.join(', ') + ']'); return imports.map(function (importedID) { var importEntry = _this6._components.get(importedID); (0, _assert2['default'])(importEntry, 'Component \'' + importedID + '\' required by \'' + id + '\' is not registered'); return importEntry; }); }).apply(this, arguments); } }, { key: '_initializeComponents', value: function _initializeComponents() { var _this7 = this; [].concat(_toConsumableArray(this._components.values())).forEach(function (entry) { entry.imports = _this7._getImportsFor(entry); }); var _promises = [].concat(_toConsumableArray(this._components.values())).map(function (_ref5) { var instance = _ref5.instance; var config = _ref5.config; var imports = _ref5.imports; var normalizedImports = _this7._normalizeImports(imports); return instance.onInit.call(instance, config, normalizedImports); }); return _Promise.all(_promises); } }, { key: '_normalizeImports', value: function _normalizeImports(imports) { var _this8 = this; return imports.map(function (importEntry) { return _defineProperty({}, importEntry.id, _this8._getExportsFor(importEntry)); }).reduce(Functors.reduce, {}); } }, { key: '_getExportsFor', value: function _getExportsFor(_ref6) { var ComponentClass = _ref6.ComponentClass; var instance = _ref6.instance; this.debug('getting imports for [' + instance.id + ']'); var exports = ComponentClass.exports || []; return exports.map(function (fnName) { return _defineProperty({}, fnName, instance[fnName].bind(instance)); }).reduce(Functors.reduce, {}); } }, { key: '_forEachComponent', value: function _forEachComponent(fnName, comparator) { for (var _len = arguments.length, args = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) { args[_key - 2] = arguments[_key]; } var components = [].concat(_toConsumableArray(this._components.values())); return components.sort(comparator).map(function (_ref7) { var instance = _ref7.instance; return instance; }).reduce(function (prev, cur) { return prev.then(function () { var _cur$fnName; return (_cur$fnName = cur[fnName]).call.apply(_cur$fnName, [cur].concat(args)); }); }, _Promise.resolve()); } }, { key: '_setStatus', value: function _setStatus(status, data) { return this.bus.emit('app.' + status, data); } }]); return Application; })(); exports['default'] = Application.create; module.exports = exports['default']; //# sourceMappingURL=application.js.map