UNPKG

typhonjs-plugin-manager

Version:

Provides a plugin manager that dispatches events to loaded plugins.

1,159 lines (957 loc) 152 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); var _getOwnPropertyNames = require('babel-runtime/core-js/object/get-own-property-names'); var _getOwnPropertyNames2 = _interopRequireDefault(_getOwnPropertyNames); var _assign = require('babel-runtime/core-js/object/assign'); var _assign2 = _interopRequireDefault(_assign); var _toConsumableArray2 = require('babel-runtime/helpers/toConsumableArray'); var _toConsumableArray3 = _interopRequireDefault(_toConsumableArray2); var _iterator25 = require('babel-runtime/core-js/symbol/iterator'); var _iterator26 = _interopRequireDefault(_iterator25); var _from = require('babel-runtime/core-js/array/from'); var _from2 = _interopRequireDefault(_from); var _keys = require('babel-runtime/core-js/object/keys'); var _keys2 = _interopRequireDefault(_keys); var _promise = require('babel-runtime/core-js/promise'); var _promise2 = _interopRequireDefault(_promise); var _getIterator2 = require('babel-runtime/core-js/get-iterator'); var _getIterator3 = _interopRequireDefault(_getIterator2); var _regenerator = require('babel-runtime/regenerator'); var _regenerator2 = _interopRequireDefault(_regenerator); var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator'); var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2); var _stringify = require('babel-runtime/core-js/json/stringify'); var _stringify2 = _interopRequireDefault(_stringify); var _map = require('babel-runtime/core-js/map'); var _map2 = _interopRequireDefault(_map); var _typeof2 = require('babel-runtime/helpers/typeof'); var _typeof3 = _interopRequireDefault(_typeof2); var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); var _createClass2 = require('babel-runtime/helpers/createClass'); var _createClass3 = _interopRequireDefault(_createClass2); var _path = require('path'); var _path2 = _interopRequireDefault(_path); var _typhonjsObjectUtil = require('typhonjs-object-util'); var _typhonjsObjectUtil2 = _interopRequireDefault(_typhonjsObjectUtil); var _EventProxy = require('backbone-esnext-events/dist/EventProxy'); var _EventProxy2 = _interopRequireDefault(_EventProxy); var _PluginEntry = require('./PluginEntry.js'); var _PluginEntry2 = _interopRequireDefault(_PluginEntry); var _PluginEvent = require('./PluginEvent.js'); var _PluginEvent2 = _interopRequireDefault(_PluginEvent); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /** * Provides a lightweight plugin manager for Node / NPM with optional `backbone-esnext-events` * integration for plugins in a safe and protected manner across NPM modules, local files, and preloaded object * instances. This pattern facilitates message passing between modules versus direct dependencies / method invocation. * * It isn't necessary to use an eventbus associated with the plugin manager though invocation then relies on invoking * methods directly with the plugin manager instance. * * When passing in an eventbus from `backbone-esnext-events` the plugin manager will register by default under these * event categories: * * `plugins:add` - {@link PluginManager#add} * * `plugins:add:all` - {@link PluginManager#addAll} * * `plugins:async:add` - {@link PluginManager#addAsync} * * `plugins:async:add:all` - {@link PluginManager#addAllAsync} * * `plugins:async:destroy:manager` - {@link PluginManager#destroyAsync} * * `plugins:async:invoke` - {@link PluginManager#invokeAsync} * * `plugins:async:invoke:event` - {@link PluginManager#invokeAsyncEvent} * * `plugins:async:remove` - {@link PluginManager#removeAsync} * * `plugins:async:remove:all` - {@link PluginManager#removeAllAsync} * * `plugins:create:event:proxy` - {@link PluginManager#createEventProxy} * * `plugins:destroy:manager` - {@link PluginManager#destroy} * * `plugins:get:all:plugin:data` - {@link PluginManager#getAllPluginData} * * `plugins:get:extra:event:data` - {@link PluginManager#getExtraEventData} * * `plugins:get:method:names` - {@link PluginManager#getMethodNames} * * `plugins:get:options` - {@link PluginManager#getOptions} * * `plugins:get:plugin:data` - {@link PluginManager#getPluginData} * * `plugins:get:plugin:enabled` - {@link PluginManager#getPluginEnabled} * * `plugins:get:plugin:event:names` - {@link PluginManager#getPluginEventNames} * * `plugins:get:plugin:method:names` - {@link PluginManager#getPluginMethodNames} * * `plugins:get:plugin:names` - {@link PluginManager#getPluginNames} * * `plugins:get:plugin:options` - {@link PluginManager#getPluginOptions} * * `plugins:get:plugins:enabled` - {@link PluginManager#getPluginsEnabled} * * `plugins:get:plugins:by:event:name` - {@link PluginManager#getPluginsByEventName} * * `plugins:get:plugins:event:names` - {@link PluginManager#getPluginsEventNames} * * `plugins:has:method` - {@link PluginManager#hasMethod} * * `plugins:has:plugin` - {@link PluginManager#hasPlugin} * * `plugins:has:plugin:method` - {@link PluginManager#hasPluginMethod} * * `plugins:invoke` - {@link PluginManager#invoke} * * `plugins:is:valid:config` - {@link PluginManager#isValidConfig} * * `plugins:remove` - {@link PluginManager#remove} * * `plugins:remove:all` - {@link PluginManager#removeAll} * * `plugins:set:extra:event:data` - {@link PluginManager#setExtraEventData} * * `plugins:set:options` - {@link PluginManager#setOptions} * * `plugins:set:plugin:enabled` - {@link PluginManager#setPluginEnabled} * * `plugins:set:plugins:enabled` - {@link PluginManager#setPluginsEnabled} * * `plugins:sync:invoke` - {@link PluginManager#invokeSync} * * `plugins:sync:invoke:event` - {@link PluginManager#invokeSyncEvent} * * Automatically when a plugin is loaded and unloaded respective callbacks `onPluginLoad` and `onPluginUnload` will * be attempted to be invoked on the plugin. This is an opportunity for the plugin to receive any associated eventbus * and wire itself into it. It should be noted that a protected proxy around the eventbus is passed to the plugins * such that when the plugin is removed automatically all events registered on the eventbus are cleaned up without * a plugin author needing to do this manually in the `onPluginUnload` callback. This solves any dangling event binding * issues. * * The plugin manager also supports asynchronous operation with the methods ending in `Async` along with event bindings * that include `async`. For asynchronous variations of `add`, `destroy`, and `remove` the lifecycle methods * `onPluginLoad` and `onPluginUnload` will be awaited on such that if a plugin returns a Promise or is an async method * then it must complete before execution continues. One can use Promises to interact with the plugin manager * asynchronously, but usage via async / await is recommended. * * If eventbus functionality is enabled it is important especially if using a process / global level eventbus such as * `backbone-esnext-eventbus` to call {@link PluginManager#destroy} to clean up all plugin eventbus resources and * the plugin manager event bindings. * * @see https://www.npmjs.com/package/backbone-esnext-events * @see https://www.npmjs.com/package/backbone-esnext-eventbus * * @example * import Events from 'backbone-esnext-events'; // Imports the TyphonEvents class for local usage. * ::or alternatively:: * import eventbus from 'backbone-esnext-eventbus'; // Imports a global / process level eventbus. * * import PluginManager from 'typhonjs-plugin-manager'; * * const pluginManager = new PluginManager({ eventbus }); * * pluginManager.add({ name: 'an-npm-plugin-enabled-module' }); * pluginManager.add({ name: 'my-local-module', target: './myModule.js' }); * * // Let's say an-npm-plugin-enabled-module responds to 'cool:event' which returns 'true'. * // Let's say my-local-module responds to 'hot:event' which returns 'false'. * // Both of the plugin / modules will have 'onPluginLoaded' invoked with a proxy to the eventbus and any plugin * // options defined. * * // One can then use the eventbus functionality to invoke associated module / plugin methods even retrieving results. * assert(eventbus.triggerSync('cool:event') === true); * assert(eventbus.triggerSync('hot:event') === false); * * // One can also indirectly invoke any method of the plugin via: * eventbus.triggerSync('plugins:invoke:sync:event', 'aCoolMethod'); // Any plugin with a method named `aCoolMethod` is invoked. * eventbus.triggerSync('plugins:invoke:sync:event', 'aCoolMethod', {}, {}, 'an-npm-plugin-enabled-module'); // specific invocation. * * // The 3rd parameter will make a copy of the hash and the 4th defines a pass through object hash sending a single * // event / object hash to the invoked method. * * // ----------------------- * * // Given that `backbone-esnext-eventbus` defines a global / process level eventbus you can import it in an entirely * // different file or even NPM module and invoke methods of loaded plugins like this: * * import eventbus from 'backbone-esnext-eventbus'; * * eventbus.triggerSync('plugins:invoke', 'aCoolMethod'); // Any plugin with a method named `aCoolMethod` is invoked. * * assert(eventbus.triggerSync('cool:event') === true); * * eventbus.trigger('plugins:remove', 'an-npm-plugin-enabled-module'); // Removes the plugin and unregisters events. * * assert(eventbus.triggerSync('cool:event') === true); // Will now fail! * * // In this case though when using the global eventbus be mindful to always call `pluginManager.destroy()` in the main * // thread of execution scope to remove all plugins and the plugin manager event bindings! */ var PluginManager = function () { /** * Instantiates PluginManager * * @param {object} [options] - Provides various configuration options: * * @param {TyphonEvents} [options.eventbus] - An instance of 'backbone-esnext-events' used as the plugin eventbus. * * @param {string} [options.eventPrepend='plugin'] - A customized name to prepend PluginManager events on the * eventbus. * * @param {boolean} [options.throwNoMethod=false] - If true then when a method fails to be invoked by any plugin * an exception will be thrown. * * @param {boolean} [options.throwNoPlugin=false] - If true then when no plugin is matched to be invoked an * exception will be thrown. * * * @param {object} [extraEventData] - Provides additional optional data to attach to PluginEvent callbacks. */ function PluginManager() { var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var extraEventData = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : void 0; (0, _classCallCheck3.default)(this, PluginManager); if ((typeof options === 'undefined' ? 'undefined' : (0, _typeof3.default)(options)) !== 'object') { throw new TypeError('\'options\' is not an object.'); } /** * Stores the plugins by name with an associated PluginEntry. * @type {Map<string, PluginEntry>} * @private */ this._pluginMap = new _map2.default(); /** * Stores any associated eventbus. * @type {TyphonEvents} * @private */ this._eventbus = null; /** * Stores any extra options / data to add to PluginEvent callbacks. * @type {Object} * @private */ this._extraEventData = extraEventData; /** * Defines options for throwing exceptions. Turned off by default. * @type {PluginManagerOptions} * @private */ this._options = { pluginsEnabled: true, noEventAdd: false, noEventDestroy: false, noEventOptions: true, noEventRemoval: false, throwNoMethod: false, throwNoPlugin: false }; if ((0, _typeof3.default)(options.eventbus) === 'object') { this.setEventbus(options.eventbus, options.eventPrepend); } this.setOptions(options); } /** * Adds a plugin by the given configuration parameters. A plugin `name` is always required. If no other options * are provided then the `name` doubles as the NPM module / local file to load. The loading first checks for an * existing `instance` to use as the plugin. Then the `target` is chosen as the NPM module / local file to load. * By passing in `options` this will be stored and accessible to the plugin during all callbacks. * * @param {PluginConfig} pluginConfig - Defines the plugin to load. * * @param {object} [moduleData] - Optional object hash to associate with plugin. * * @returns {PluginData|undefined} */ (0, _createClass3.default)(PluginManager, [{ key: 'add', value: function add(pluginConfig, moduleData) { if (this._pluginMap === null) { throw new ReferenceError('This PluginManager instance has been destroyed.'); } if ((typeof pluginConfig === 'undefined' ? 'undefined' : (0, _typeof3.default)(pluginConfig)) !== 'object') { throw new TypeError('\'pluginConfig\' is not an \'object\'.'); } if (typeof pluginConfig.name !== 'string') { throw new TypeError('\'pluginConfig.name\' is not a \'string\' for entry: ' + (0, _stringify2.default)(pluginConfig) + '.'); } if (typeof pluginConfig.target !== 'undefined' && typeof pluginConfig.target !== 'string') { throw new TypeError('\'pluginConfig.target\' is not a string for entry: ' + (0, _stringify2.default)(pluginConfig) + '.'); } if (typeof pluginConfig.options !== 'undefined' && (0, _typeof3.default)(pluginConfig.options) !== 'object') { throw new TypeError('\'pluginConfig.options\' is not an \'object\' for entry: ' + (0, _stringify2.default)(pluginConfig) + '.'); } if (typeof moduleData !== 'undefined' && (typeof moduleData === 'undefined' ? 'undefined' : (0, _typeof3.default)(moduleData)) !== 'object') { throw new TypeError('\'moduleData\' is not an \'object\' for entry: ' + (0, _stringify2.default)(pluginConfig) + '.'); } // If a plugin with the same name already exists post a warning and exit early. if (this._pluginMap.has(pluginConfig.name)) { // Please note that a plugin or other logger must be setup on the associated eventbus. if (this._eventbus !== null && typeof this._eventbus !== 'undefined') { this._eventbus.trigger('log:warn', 'A plugin already exists with name: ' + pluginConfig.name + '.'); } return void 0; } var instance = void 0, target = void 0, type = void 0; // Use an existing instance of a plugin; a static class is assumed when instance is a function. if ((0, _typeof3.default)(pluginConfig.instance) === 'object' || typeof pluginConfig.instance === 'function') { instance = pluginConfig.instance; target = pluginConfig.name; type = 'instance'; } else { // If a target is defined use it instead of the name. target = pluginConfig.target || pluginConfig.name; if (target.match(/^[.\/\\]/)) { instance = require(_path2.default.resolve(target)); // eslint-disable global-require type = 'require-path'; } else { instance = require(target); // eslint-disable global-require type = 'require-module'; } } // Create an object hash with data describing the plugin, manager, and any extra module data. var pluginData = JSON.parse((0, _stringify2.default)({ manager: { eventPrepend: this._eventPrepend }, module: moduleData || {}, plugin: { name: pluginConfig.name, scopedName: this._eventPrepend + ':' + pluginConfig.name, target: target, targetEscaped: _PluginEntry2.default.escape(target), type: type, options: pluginConfig.options || {} } })); _typhonjsObjectUtil2.default.deepFreeze(pluginData, ['eventPrepend', 'scopedName']); var eventProxy = this._eventbus !== null && typeof this._eventbus !== 'undefined' ? new _EventProxy2.default(this._eventbus) : void 0; var entry = new _PluginEntry2.default(pluginConfig.name, pluginData, instance, eventProxy); this._pluginMap.set(pluginConfig.name, entry); // Invoke private module method which allows skipping optional error checking. s_INVOKE_SYNC_EVENTS('onPluginLoad', {}, {}, this._extraEventData, pluginConfig.name, this._pluginMap, this._options, false); // Invoke `typhonjs:plugin:manager:plugin:added` allowing external code to react to plugin addition. if (this._eventbus) { this._eventbus.trigger('typhonjs:plugin:manager:plugin:added', pluginData); } return pluginData; } /** * Adds a plugin by the given configuration parameters. A plugin `name` is always required. If no other options * are provided then the `name` doubles as the NPM module / local file to load. The loading first checks for an * existing `instance` to use as the plugin. Then the `target` is chosen as the NPM module / local file to load. * By passing in `options` this will be stored and accessible to the plugin during all callbacks. * * @param {PluginConfig} pluginConfig - Defines the plugin to load. * * @param {object} [moduleData] - Optional object hash to associate with plugin. * * @returns {Promise<PluginData|undefined>} */ }, { key: 'addAsync', value: function () { var _ref = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee(pluginConfig, moduleData) { var instance, target, type, pluginData, eventProxy, entry; return _regenerator2.default.wrap(function _callee$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: if (!(this._pluginMap === null)) { _context.next = 2; break; } throw new ReferenceError('This PluginManager instance has been destroyed.'); case 2: if (!((typeof pluginConfig === 'undefined' ? 'undefined' : (0, _typeof3.default)(pluginConfig)) !== 'object')) { _context.next = 4; break; } throw new TypeError('\'pluginConfig\' is not an \'object\'.'); case 4: if (!(typeof pluginConfig.name !== 'string')) { _context.next = 6; break; } throw new TypeError('\'pluginConfig.name\' is not a \'string\' for entry: ' + (0, _stringify2.default)(pluginConfig) + '.'); case 6: if (!(typeof pluginConfig.target !== 'undefined' && typeof pluginConfig.target !== 'string')) { _context.next = 8; break; } throw new TypeError('\'pluginConfig.target\' is not a string for entry: ' + (0, _stringify2.default)(pluginConfig) + '.'); case 8: if (!(typeof pluginConfig.options !== 'undefined' && (0, _typeof3.default)(pluginConfig.options) !== 'object')) { _context.next = 10; break; } throw new TypeError('\'pluginConfig.options\' is not an \'object\' for entry: ' + (0, _stringify2.default)(pluginConfig) + '.'); case 10: if (!(typeof moduleData !== 'undefined' && (typeof moduleData === 'undefined' ? 'undefined' : (0, _typeof3.default)(moduleData)) !== 'object')) { _context.next = 12; break; } throw new TypeError('\'moduleData\' is not an \'object\' for entry: ' + (0, _stringify2.default)(pluginConfig) + '.'); case 12: if (!this._pluginMap.has(pluginConfig.name)) { _context.next = 15; break; } // Please note that a plugin or other logger must be setup on the associated eventbus. if (this._eventbus !== null && typeof this._eventbus !== 'undefined') { this._eventbus.trigger('log:warn', 'A plugin already exists with name: ' + pluginConfig.name + '.'); } return _context.abrupt('return', void 0); case 15: instance = void 0, target = void 0, type = void 0; // Use an existing instance of a plugin; a static class is assumed when instance is a function. if ((0, _typeof3.default)(pluginConfig.instance) === 'object' || typeof pluginConfig.instance === 'function') { instance = pluginConfig.instance; target = pluginConfig.name; type = 'instance'; } else { // If a target is defined use it instead of the name. target = pluginConfig.target || pluginConfig.name; if (target.match(/^[.\/\\]/)) { instance = require(_path2.default.resolve(target)); // eslint-disable global-require type = 'require-path'; } else { instance = require(target); // eslint-disable global-require type = 'require-module'; } } // Create an object hash with data describing the plugin, manager, and any extra module data. pluginData = JSON.parse((0, _stringify2.default)({ manager: { eventPrepend: this._eventPrepend }, module: moduleData || {}, plugin: { name: pluginConfig.name, scopedName: this._eventPrepend + ':' + pluginConfig.name, target: target, targetEscaped: _PluginEntry2.default.escape(target), type: type, options: pluginConfig.options || {} } })); _typhonjsObjectUtil2.default.deepFreeze(pluginData, ['eventPrepend', 'scopedName']); eventProxy = this._eventbus !== null && typeof this._eventbus !== 'undefined' ? new _EventProxy2.default(this._eventbus) : void 0; entry = new _PluginEntry2.default(pluginConfig.name, pluginData, instance, eventProxy); this._pluginMap.set(pluginConfig.name, entry); // Invoke private module method which allows skipping optional error checking. _context.next = 24; return s_INVOKE_ASYNC_EVENTS('onPluginLoad', {}, {}, this._extraEventData, pluginConfig.name, this._pluginMap, this._options, false); case 24: if (!this._eventbus) { _context.next = 27; break; } _context.next = 27; return this._eventbus.triggerAsync('typhonjs:plugin:manager:plugin:added', pluginData); case 27: return _context.abrupt('return', pluginData); case 28: case 'end': return _context.stop(); } } }, _callee, this); })); function addAsync(_x3, _x4) { return _ref.apply(this, arguments); } return addAsync; }() /** * Initializes multiple plugins in a single call. * * @param {Array<PluginConfig>} pluginConfigs - An array of plugin config object hash entries. * * @param {object} [moduleData] - Optional object hash to associate with all plugins. * * @returns {Array<PluginData>} */ }, { key: 'addAll', value: function addAll() { var pluginConfigs = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; var moduleData = arguments[1]; if (this._pluginMap === null) { throw new ReferenceError('This PluginManager instance has been destroyed.'); } if (!Array.isArray(pluginConfigs)) { throw new TypeError('\'plugins\' is not an array.'); } var pluginsData = []; var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = (0, _getIterator3.default)(pluginConfigs), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var pluginConfig = _step.value; var result = this.add(pluginConfig, moduleData); if (result) { pluginsData.push(result); } } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } return pluginsData; } /** * Initializes multiple plugins in a single call. * * @param {Array<PluginConfig>} pluginConfigs - An array of plugin config object hash entries. * * @param {object} [moduleData] - Optional object hash to associate with all plugins. * * @returns {Promise<Array<PluginData>>} */ }, { key: 'addAllAsync', value: function addAllAsync() { var pluginConfigs = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; var moduleData = arguments[1]; if (this._pluginMap === null) { throw new ReferenceError('This PluginManager instance has been destroyed.'); } if (!Array.isArray(pluginConfigs)) { throw new TypeError('\'plugins\' is not an array.'); } var pluginsData = []; var _iteratorNormalCompletion2 = true; var _didIteratorError2 = false; var _iteratorError2 = undefined; try { for (var _iterator2 = (0, _getIterator3.default)(pluginConfigs), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { var pluginConfig = _step2.value; var result = this.addAsync(pluginConfig, moduleData); if (result) { pluginsData.push(result); } } } catch (err) { _didIteratorError2 = true; _iteratorError2 = err; } finally { try { if (!_iteratorNormalCompletion2 && _iterator2.return) { _iterator2.return(); } } finally { if (_didIteratorError2) { throw _iteratorError2; } } } return _promise2.default.all(pluginsData); } /** * Provides the eventbus callback which may prevent addition if optional `noEventAdd` is enabled. This disables * the ability for plugins to be added via events preventing any external code adding plugins in this manner. * * @param {PluginConfig} pluginConfig - Defines the plugin to load. * * @param {object} [moduleData] - Optional object hash to associate with all plugins. * * @returns {PluginData|undefined} - Operation success. * @private */ }, { key: '_addEventbus', value: function _addEventbus(pluginConfig, moduleData) { if (this._pluginMap === null) { throw new ReferenceError('This PluginManager instance has been destroyed.'); } return !this._options.noEventAdd ? this.add(pluginConfig, moduleData) : void 0; } /** * Provides the eventbus callback which may prevent addition if optional `noEventAdd` is enabled. This disables * the ability for plugins to be added via events preventing any external code adding plugins in this manner. * * @param {PluginConfig} pluginConfig - Defines the plugin to load. * * @param {object} [moduleData] - Optional object hash to associate with all plugins. * * @returns {Promise<PluginData|undefined>} - Operation success. * @private */ }, { key: '_addEventbusAsync', value: function _addEventbusAsync(pluginConfig, moduleData) { if (this._pluginMap === null) { throw new ReferenceError('This PluginManager instance has been destroyed.'); } return !this._options.noEventAdd ? this.addAsync(pluginConfig, moduleData) : _promise2.default.resolve(void 0); } /** * Provides the eventbus callback which may prevent addition if optional `noEventAdd` is enabled. This disables * the ability for plugins to be added via events preventing any external code adding plugins in this manner. * * @param {Array<PluginConfig>} pluginConfigs - An array of plugin config object hash entries. * * @param {object} [moduleData] - Optional object hash to associate with all plugins. * * @returns {Array<PluginData>} * @private */ }, { key: '_addAllEventbus', value: function _addAllEventbus(pluginConfigs, moduleData) { if (this._pluginMap === null) { throw new ReferenceError('This PluginManager instance has been destroyed.'); } if (!this._options.noEventAdd) { return this.addAll(pluginConfigs, moduleData); } } /** * Provides the eventbus callback which may prevent addition if optional `noEventAdd` is enabled. This disables * the ability for plugins to be added via events preventing any external code adding plugins in this manner. * * @param {Array<PluginConfig>} pluginConfigs - An array of plugin config object hash entries. * * @param {object} [moduleData] - Optional object hash to associate with all plugins. * * @returns {Promise<Array<PluginData>>} * @private */ }, { key: '_addAllEventbusAsync', value: function _addAllEventbusAsync(pluginConfigs, moduleData) { if (this._pluginMap === null) { throw new ReferenceError('This PluginManager instance has been destroyed.'); } if (!this._options.noEventAdd) { return this.addAllAsync(pluginConfigs, moduleData); } } /** * If an eventbus is assigned to this plugin manager then a new EventProxy wrapping this eventbus is returned. * * @returns {EventProxy} */ }, { key: 'createEventProxy', value: function createEventProxy() { return this._eventbus !== null ? new _EventProxy2.default(this._eventbus) : void 0; } /** * Destroys all managed plugins after unloading them. */ }, { key: 'destroy', value: function destroy() { if (this._pluginMap === null) { throw new ReferenceError('This PluginManager instance has been destroyed.'); } this.removeAll(); if (this._eventbus !== null && typeof this._eventbus !== 'undefined') { this._eventbus.off(this._eventPrepend + ':add', this._addEventbus, this); this._eventbus.off(this._eventPrepend + ':add:all', this._addAllEventbus, this); this._eventbus.off(this._eventPrepend + ':async:add', this._addEventbusAsync, this); this._eventbus.off(this._eventPrepend + ':async:add:all', this._addAllEventbusAsync, this); this._eventbus.off(this._eventPrepend + ':async:destroy:manager', this._destroyEventbusAsync, this); this._eventbus.off(this._eventPrepend + ':async:invoke', this.invokeAsync, this); this._eventbus.off(this._eventPrepend + ':async:invoke:event', this.invokeAsyncEvent, this); this._eventbus.off(this._eventPrepend + ':async:remove', this._removeEventbusAsync, this); this._eventbus.off(this._eventPrepend + ':async:remove:all', this._removeAllEventbusAsync, this); this._eventbus.off(this._eventPrepend + ':create:event:proxy', this.createEventProxy, this); this._eventbus.off(this._eventPrepend + ':destroy:manager', this._destroyEventbus, this); this._eventbus.off(this._eventPrepend + ':get:all:plugin:data', this.getAllPluginData, this); this._eventbus.off(this._eventPrepend + ':get:extra:event:data', this.getExtraEventData, this); this._eventbus.off(this._eventPrepend + ':get:method:names', this.getMethodNames, this); this._eventbus.off(this._eventPrepend + ':get:options', this.getOptions, this); this._eventbus.off(this._eventPrepend + ':get:plugin:enabled', this.getPluginEnabled, this); this._eventbus.off(this._eventPrepend + ':get:plugin:data', this.getPluginData, this); this._eventbus.off(this._eventPrepend + ':get:plugin:event:names', this.getPluginEventNames, this); this._eventbus.off(this._eventPrepend + ':get:plugin:method:names', this.getPluginMethodNames, this); this._eventbus.off(this._eventPrepend + ':get:plugin:names', this.getPluginNames, this); this._eventbus.off(this._eventPrepend + ':get:plugin:options', this.getPluginOptions, this); this._eventbus.off(this._eventPrepend + ':get:plugins:enabled', this.getPluginsEnabled, this); this._eventbus.off(this._eventPrepend + ':get:plugins:by:event:name', this.getPluginsByEventName, this); this._eventbus.off(this._eventPrepend + ':get:plugins:event:names', this.getPluginsEventNames, this); this._eventbus.off(this._eventPrepend + ':has:method', this.hasMethod, this); this._eventbus.off(this._eventPrepend + ':has:plugin', this.hasPlugin, this); this._eventbus.off(this._eventPrepend + ':has:plugin:method', this.hasPluginMethod, this); this._eventbus.off(this._eventPrepend + ':invoke', this.invoke, this); this._eventbus.off(this._eventPrepend + ':is:valid:config', this.isValidConfig, this); this._eventbus.off(this._eventPrepend + ':remove', this._removeEventbus, this); this._eventbus.off(this._eventPrepend + ':remove:all', this._removeAllEventbus, this); this._eventbus.off(this._eventPrepend + ':set:extra:event:data', this.setExtraEventData, this); this._eventbus.off(this._eventPrepend + ':set:options', this._setOptionsEventbus, this); this._eventbus.off(this._eventPrepend + ':set:plugin:enabled', this.setPluginEnabled, this); this._eventbus.off(this._eventPrepend + ':set:plugins:enabled', this.setPluginsEnabled, this); this._eventbus.off(this._eventPrepend + ':sync:invoke', this.invokeSync, this); this._eventbus.off(this._eventPrepend + ':sync:invoke:event', this.invokeSyncEvent, this); } this._pluginMap = null; this._eventbus = null; } /** * Destroys all managed plugins after unloading them. */ }, { key: 'destroyAsync', value: function () { var _ref2 = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee2() { return _regenerator2.default.wrap(function _callee2$(_context2) { while (1) { switch (_context2.prev = _context2.next) { case 0: if (!(this._pluginMap === null)) { _context2.next = 2; break; } throw new ReferenceError('This PluginManager instance has been destroyed.'); case 2: _context2.next = 4; return this.removeAll(); case 4: if (this._eventbus !== null && typeof this._eventbus !== 'undefined') { this._eventbus.off(this._eventPrepend + ':add', this._addEventbus, this); this._eventbus.off(this._eventPrepend + ':add:all', this._addAllEventbus, this); this._eventbus.off(this._eventPrepend + ':async:add', this._addEventbusAsync, this); this._eventbus.off(this._eventPrepend + ':async:add:all', this._addAllEventbusAsync, this); this._eventbus.off(this._eventPrepend + ':async:destroy:manager', this._destroyEventbusAsync, this); this._eventbus.off(this._eventPrepend + ':async:invoke', this.invokeAsync, this); this._eventbus.off(this._eventPrepend + ':async:invoke:event', this.invokeAsyncEvent, this); this._eventbus.off(this._eventPrepend + ':async:remove', this._removeEventbusAsync, this); this._eventbus.off(this._eventPrepend + ':async:remove:all', this._removeAllEventbusAsync, this); this._eventbus.off(this._eventPrepend + ':create:event:proxy', this.createEventProxy, this); this._eventbus.off(this._eventPrepend + ':destroy:manager', this._destroyEventbus, this); this._eventbus.off(this._eventPrepend + ':get:all:plugin:data', this.getAllPluginData, this); this._eventbus.off(this._eventPrepend + ':get:extra:event:data', this.getExtraEventData, this); this._eventbus.off(this._eventPrepend + ':get:method:names', this.getMethodNames, this); this._eventbus.off(this._eventPrepend + ':get:options', this.getOptions, this); this._eventbus.off(this._eventPrepend + ':get:plugin:enabled', this.getPluginEnabled, this); this._eventbus.off(this._eventPrepend + ':get:plugin:data', this.getPluginData, this); this._eventbus.off(this._eventPrepend + ':get:plugin:event:names', this.getPluginEventNames, this); this._eventbus.off(this._eventPrepend + ':get:plugin:method:names', this.getPluginMethodNames, this); this._eventbus.off(this._eventPrepend + ':get:plugin:names', this.getPluginNames, this); this._eventbus.off(this._eventPrepend + ':get:plugin:options', this.getPluginOptions, this); this._eventbus.off(this._eventPrepend + ':get:plugins:enabled', this.getPluginsEnabled, this); this._eventbus.off(this._eventPrepend + ':get:plugins:by:event:name', this.getPluginsByEventName, this); this._eventbus.off(this._eventPrepend + ':get:plugins:event:names', this.getPluginsEventNames, this); this._eventbus.off(this._eventPrepend + ':has:method', this.hasMethod, this); this._eventbus.off(this._eventPrepend + ':has:plugin', this.hasPlugin, this); this._eventbus.off(this._eventPrepend + ':has:plugin:method', this.hasPluginMethod, this); this._eventbus.off(this._eventPrepend + ':invoke', this.invoke, this); this._eventbus.off(this._eventPrepend + ':is:valid:config', this.isValidConfig, this); this._eventbus.off(this._eventPrepend + ':remove', this._removeEventbus, this); this._eventbus.off(this._eventPrepend + ':remove:all', this._removeAllEventbus, this); this._eventbus.off(this._eventPrepend + ':set:extra:event:data', this.setExtraEventData, this); this._eventbus.off(this._eventPrepend + ':set:options', this._setOptionsEventbus, this); this._eventbus.off(this._eventPrepend + ':set:plugin:enabled', this.setPluginEnabled, this); this._eventbus.off(this._eventPrepend + ':set:plugins:enabled', this.setPluginsEnabled, this); this._eventbus.off(this._eventPrepend + ':sync:invoke', this.invokeSync, this); this._eventbus.off(this._eventPrepend + ':sync:invoke:event', this.invokeSyncEvent, this); } this._pluginMap = null; this._eventbus = null; case 7: case 'end': return _context2.stop(); } } }, _callee2, this); })); function destroyAsync() { return _ref2.apply(this, arguments); } return destroyAsync; }() /** * Provides the eventbus callback which may prevent plugin manager destruction if optional `noEventDestroy` is * enabled. This disables the ability for the plugin manager to be destroyed via events preventing any external * code removing plugins in this manner. * * @private */ }, { key: '_destroyEventbus', value: function _destroyEventbus() { if (this._pluginMap === null) { throw new ReferenceError('This PluginManager instance has been destroyed.'); } if (!this._options.noEventDestroy) { this.destroy(); } } /** * Provides the eventbus callback which may prevent plugin manager destruction if optional `noEventDestroy` is * enabled. This disables the ability for the plugin manager to be destroyed via events preventing any external * code removing plugins in this manner. * * @private */ }, { key: '_destroyEventbusAsync', value: function () { var _ref3 = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee3() { return _regenerator2.default.wrap(function _callee3$(_context3) { while (1) { switch (_context3.prev = _context3.next) { case 0: if (!(this._pluginMap === null)) { _context3.next = 2; break; } throw new ReferenceError('This PluginManager instance has been destroyed.'); case 2: if (this._options.noEventDestroy) { _context3.next = 5; break; } _context3.next = 5; return this.destroyAsync(); case 5: case 'end': return _context3.stop(); } } }, _callee3, this); })); function _destroyEventbusAsync() { return _ref3.apply(this, arguments); } return _destroyEventbusAsync; }() /** * Returns the enabled state of a plugin. * * @param {string} pluginName - Plugin name to set state. * * @returns {boolean} - Operation success. */ }, { key: 'getPluginEnabled', value: function getPluginEnabled(pluginName) { if (this._pluginMap === null) { throw new ReferenceError('This PluginManager instance has been destroyed.'); } if (typeof pluginName !== 'string') { throw new TypeError('\'pluginName\' is not a string.'); } var entry = this._pluginMap.get(pluginName); return entry instanceof _PluginEntry2.default && entry.enabled; } /** * Returns the event binding names registered on any associated plugin EventProxy. * * @param {string} pluginName - Plugin name to set state. * * @returns {string[]} - Event binding names registered from the plugin. */ }, { key: 'getPluginEventNames', value: function getPluginEventNames(pluginName) { if (this._pluginMap === null) { throw new ReferenceError('This PluginManager instance has been destroyed.'); } if (typeof pluginName !== 'string') { throw new TypeError('\'pluginName\' is not a string.'); } var entry = this._pluginMap.get(pluginName); return entry instanceof _PluginEntry2.default && entry._eventProxy ? entry._eventProxy.getEventNames() : []; } /** * Returns the enabled state of a list of plugins. * * @param {Array<string>} pluginNames - An array / iterable of plugin names. * * @returns {Array<{pluginName: string, enabled: boolean}>} A list of objects with plugin name and enabled state. */ }, { key: 'getPluginsEnabled', value: function getPluginsEnabled(pluginNames) { if (this._pluginMap === null) { throw new ReferenceError('This PluginManager instance has been destroyed.'); } var results = []; var _iteratorNormalCompletion3 = true; var _didIteratorError3 = false; var _iteratorError3 = undefined; try { for (var _iterator3 = (0, _getIterator3.default)(pluginNames), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { var pluginName = _step3.value; results.push({ pluginName: pluginName, enabled: this.getPluginEnabled(pluginName) }); } } catch (err) { _didIteratorError3 = true; _iteratorError3 = err; } finally { try { if (!_iteratorNormalCompletion3 && _iterator3.return) { _iterator3.return(); } } finally { if (_didIteratorError3) { throw _iteratorError3; } } } return results; } /** * Returns the event binding names registered from each plugin. * * @param {string|string[]} [nameOrList] - An array / iterable of plugin names. * * @returns {Array<{pluginName: string, events: string[]}>} A list of objects with plugin name and event binding * names registered from the plugin. */ }, { key: 'getPluginsEventNames', value: function getPluginsEventNames(nameOrList) { if (this._pluginMap === null) { throw new ReferenceError('This PluginManager instance has been destroyed.'); } if (typeof nameOrList === 'undefined') { nameOrList = this._pluginMap.keys(); } if (typeof nameOrList === 'string') { nameOrList = [nameOrList]; } var results = []; var _iteratorNormalCompletion4 = true; var _didIteratorError4 = false; var _iteratorError4 = undefined; try { for (var _iterator4 = (0, _getIterator3.default)(nameOrList), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) { var pluginName = _step4.value; results.push({ pluginName: pluginName, events: this.getPluginEventNames(pluginName) }); } } catch (err) { _didIteratorError4 = true; _iteratorError4 = err; } finally { try { if (!_iteratorNormalCompletion4 && _iterator4.return) { _iterator4.return(); } }