typhonjs-plugin-manager
Version:
Provides a plugin manager that dispatches events to loaded plugins.
1,159 lines (957 loc) • 152 kB
JavaScript
'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();
}
}