Home Manual Reference Source Test Repository

typhonjs-plugin-manager

NPM Documentation Code Style License Gitter

Build Status Coverage Dependency Status

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:create:event:proxy - invokes PluginManager#createEventProxy

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:invoke:async - invokes PluginManager#invokeAsync

plugins:invoke:sync - invokes PluginManager#invokeSync

plugins:invoke:sync:event - invokes PluginManager#invokeSyncEvent

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

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.

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!