A simple Event Emitter, tuned to be used as a plugin driver.
"Plugin Use" is a small module which you can use to make a plugin system for a project. It essentially creates an Event Emitter with special properties which make it useful for writing encapsulated plugins:
- Individual plugins can't subscribe to their own events emitted, only events from other plugins.
- While each plugin gets its own event emitter, all plugins receive events from other registered plugins.
- Every emitted event is stored in a buffer, and replayed to newly registered plugins, so they can handle past messages.
- The central library can mutate any emitted events, allowing it to coerce values or change behaviours (e.g. throw on an
error
event with no listeners). - Plugins can also pass a plain object, which the library can decide what to do with.
A simple EventEmitter, such as the one provided by default in Node.js is not sufficient for a plugin system as some events need to be special cased.
plugin-use
is available on npm. To install it, type:
$ npm install plugin-use
plugin-use
can be imported with the following line:
import createUse from 'https://deno.land/x/[email protected]/index.ts'
The primary export of plugin-use
is the createUse
function that can gets called by the library that wants a plugin system:
import createUse from 'plugin-use'
// Create a mapping of well known events and the arguments they have, this will help
// for TypeScript type checking:
interface EmitMap {
'error': [Error],
'assert': [boolean],
}
// Create a type which represents the value of each property in the object pattern
type PluginObjectValue = (...args: any[]) => any
const use = createUse<EmitMap, PluginObjectValue>({
// If a plugin is instantiated with a plain object, then `handleObject` is
// called for each property, allowing you to map them to events to be emitted
handleObject(emitter: Emitter<EmitMap>, key: string | number, value: PluginObjectValue) {
emitter.emit(key, value)
},
// We can special case some events by using `handleEmit`. Return `true` to
// mark this event as handled, meaning it won't be emitted by this library.
handleEmit<K extends keyof EmitMap>(emitter: Emitter<EmitMap>, name: K, ...args: EmitMap[K]): boolean {
// Throw on an `error` event if there are no listeners for it, just like NodeJS!
if (name === 'error' && emitter.count('error') === 0) {
throw args[0]
}
// Let this library handle the rest of the events.
return false
},
// If a plugin function returns a value, then `handleReturn` will be called to deal with it.
handleReturn(emitter: Emitter<EmitMap>, value: any) {
emitter.emit('assert', value)
},
})