pluggi v1.0.0
pluggi.js
Base for building modularised apps with plugins
Current status
What's it for?
An easy way create apps which accept plugins.
A plugin is just a function which is executed and passed the app instance and options.
It's dead simple but powerful.
Usage
const Pluggi = require('pluggi');
const app = new Pluggi( {
myPlugin: { a: 1 }
} );
app.plugin( 'myPlugin', function(options) {
assert(this == app); // Plugin called with app instance
assert(options.a == 1); // Options keyed with the name of the plugin are passed
} );Plugin loading methods
API: .plugin( [name], [plugin], [options] )
Name + plugin function
app.plugin( 'myPlugin', function(options) {
/* ... */
} );Named function
If function is named, plugin name is taken from the function name. This is equivalent to the above example:
app.plugin( function myPlugin(options) {
/* ... */
} );Module name
If just name is provided, the plugin is loaded as a Node module.
app.plugin( 'my-amazing-plugin' );
assert(app.plugins.myAmazingPlugin);This calls require('my-amazing-plugin') internally. Note that the plugin name is converted to camel case.
Plugin registry
The app has a property .plugins which contains a registry of all loaded plugins.
The value for each property of .plugins is the return value from the plugin function.
app.plugin( 'myPlugin', () => ({ aProp: 123 }) );
assert(app.plugins.myPlugin.aProp == 123);Return values must be truthy, or an empty object {} is used.
Subclassing
Let's say you're making a web framework called "monkey". You want users to be able to develop plugins for this framework.
Subclass Pluggi and set [PLUGIN_PREFIX] to 'monkey'.
const {PLUGIN_PREFIX} = Pluggi;
class Monkey extends Pluggi {
constructor(options) {
super(options);
this[PLUGIN_PREFIX] = 'monkey';
}
}Now users can develop plugins published on npm as 'monkey-routes', 'monkey-express' etc. When a plugin is loaded by name only, 'monkey-' is added to the start of the module name before it is require()-ed.
const app = new Monkey();
app.plugin('router')
.plugin('express');
assert(app.plugins.router);
assert(app.plugins.express);The plugins are registered as router and express, but the npm modules which were required were called 'monkey-router' and 'monkey-express'.
NB PLUGIN_PREFIX is currently a string, but it may be changed to a Symbol in a future version. This will not be considered a semver major change, so always use it via Pluggi.PLUGIN_PREFIX.
Options
Options are set for a plugin in 2 places - local and global. The two are merged when passed to the plugin.
const app = new Pluggi( {
router: { globalOpt: 123 }
} );
// Here is our plugin function
function router(options) {
console.log(options);
}
app.plugin( router, { localOpt: 456 } );
// Logs { globalOpt: 123, localOpt: 456 }Global options are recorded on the app as app.options.
Namespacing
Plugins will typically alter properties on the app.
To ensure two plugins do not clash, it is recommended that they respect a namespace defined by the name of the plugin. They should only make changes in two ways:
- Set property on app named with plugin name
- Return a value to be stored in the
app.pluginsobject
// Example plugin
function router(options) {
this.router = ...
return { bindToExpress: function() { ... } };
}The bindToExpress() method can now be accessed at app.plugins.router.bindToExpress().
Tests
Use npm test to run the tests. Use npm run cover to check coverage.
Changelog
See changelog.md
Issues
If you discover a bug, please raise an issue on Github. https://github.com/overlookmotel/pluggi/issues
Contribution
Pull requests are very welcome. Please:
- ensure all tests pass before submitting PR
- add an entry to changelog
- add tests for new features
- document new functionality/API additions in README
7 years ago