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
- invokes PluginManager#add
plugins:add:all
- invokes PluginManager#addAll
plugins:async:add
- invokes PluginManager#addAsync
plugins:async:add:all
- invokes PluginManager#addAllAsync
plugins:async:destroy:manager
- invokes PluginManager#destroyAsync
plugins:async:invoke
- invokes PluginManager#invokeAsync
plugins:async:invoke:event
- invokes PluginManager#invokeAsyncEvent
plugins:async:remove
- invokes PluginManager#removeAsync
plugins:async:remove:all
- invokes PluginManager#removeAllAsync
plugins:create:event:proxy
- invokes PluginManager#createEventProxy
plugins:destroy:manager
- invokes PluginManager#destroy
plugins:get:all:plugin:data
- invokes PluginManager#getAllPluginData
plugins:get:extra:event:data
- invokes PluginManager#getExtraEventData
plugins:get:method:names
- invokes PluginManager#getMethodNames
plugins:get:options
- invokes PluginManager#getOptions
plugins:get:plugin:data
- invokes PluginManager#getPluginData
plugins:get:plugin:enabled
- invokes PluginManager#getPluginEnabled
plugins:get:plugin:method:names
- invokes PluginManager#getPluginMethodNames
plugins:get:plugin:names
- invokes PluginManager#getPluginNames
plugins:get:plugin:options
- invokes PluginManager#getPluginOptions
plugins:get:plugins:enabled
- invokes PluginManager#getPluginsEnabled
plugins:has:method
- invokes PluginManager#hasMethod
plugins:has:plugin
- invokes PluginManager#hasPlugin
plugins:has:plugin:method
- invokes PluginManager#hasPluginMethod
plugins:invoke
- invokes PluginManager#invoke
plugins:is:valid:config
- invokes PluginManager#isValidConfig
plugins:remove
- invokes PluginManager#remove
plugins:remove:all
- invokes PluginManager#removeAll
plugins:set:extra:event:data
- invokes PluginManager#setExtraEventData
plugins:set:plugin:enabled
- invokes PluginManager#setPluginEnabled
plugins:set:plugins:enabled
- invokes PluginManager#setPluginsEnabled
plugins:sync:invoke
- invokes PluginManager#invokeSync
plugins:sync:invoke:event
- invokes 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 PluginManager#destroy to clean up all plugin eventbus resources and
the plugin manager event bindings.
Please see the following NPM modules for eventbus info:
Examples follow:
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 defines a pass through object hash and the 4th will make a copy of the 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!