fixings
Version:
A general-purpose plugin system to add your own plugin system to any project
139 lines (127 loc) • 3.56 kB
JavaScript
/**
* Fixings/Eat class
*
* Eat (consumer) class for running a set of hooks.
*
* @version 1.0.0
* @author Drew Sommer
* @license MIT
*/
/** @ignore */
const DepTree = require('deptree')
class Eat {
/**
* Create a new hook consumer (do not create directly, use Fixings class)
* @param {Hook} hook - The hook to iterate over
* @param {String} name - Variable name for plugin $name
* @param {String} before - Variable name for plugin $before
* @param {String} after - Variable name for plugin $after
* @example
* var Eater = new Eat(...)
*/
constructor(hook, name, before, after) {
this.hook = hook
this.cur = 0
// Create a new dependency resolver
var depTree = new DepTree()
// Add dependencies for afters and befores
for (var plugin of this.hook.plugins) {
depTree.add(plugin[name])
if (plugin[after]) depTree.add(plugin[name], plugin[after])
if (plugin[before]) depTree.add(plugin[before], plugin[name])
}
var resolved = depTree.resolve()
// Update hook ordering
for (var n of resolved) {
for (let i=0; i<this.hook.plugins.length; i++) {
if (n === this.hook.plugins[i][name]) {
this.hook.order.push(i)
break
}
}
}
// Fill in missing plugins in order (they come after any ordered plugins)
var accounted = []
for (let i of this.hook.order) {
accounted.push(this.hook.plugins[i][name])
}
for (let i=0; i<this.hook.plugins.length; i++) {
if(!accounted.includes(this.hook.plugins[i][name])) {
this.hook.order.push(i)
accounted.push(this.hook.plugins[i][name])
}
}
}
/**
* Get the next plugin in the hook
* @returns {?PluginHook} Returns the plugin to run, or null if all plugins ran
* @example
* var plugin = Eater.next()
* while (plugin) {
* plugin.handler(...)
* plugin = Eater.next()
* }
*/
next() {
if (this.cur >= this.hook.order.length) return null
var plugin = this.hook.plugins[this.hook.order[this.cur]]
this.cur++
return plugin
}
/**
* @typedef {function(nextCallback)}
* @callback
* @param {?Error} - Error message or null (if no error)
* @param {?PluginHook} - Plugin hook to run, or null if all plugins ran
*/
/**
* Get the next plugin in the hook asyncronously
* @param {nextCallback} [callback=null] - Callback function
* @async
* @returns {?PluginHook} - Plugin hook to run, or null if all plugins ran
* @throws {Error} - If an error occured
* @example
* // Not recommended
* Eater.nextAsync((error, plugin) => {
* plugin.handler(...)
* Eater.nextAsync((error, plugin) => {...})
* })
* @example
* var plugin = await Eater.nextAsync()
* while (plugin) {
* plugin.handler(...)
* plugin = await Eater.nextAsync()
* }
* @example
* var plugin
* Eater.nextAsync()
* .then(plug => { plugin = plug })
* .catch(...)
* while(plugin) {
* plugin.handler(...)
* Eater.nextAsync()
* .then(plug => { plugin = plug })
* .catch(...)
* }
*/
nextAsync(callback=null) {
if (typeof callback === 'function') {
setImmediate(() => {
try {
callback(null, this.next())
} catch (error) {
callback(error)
}
})
} else {
return new Promise((resolve, reject) => {
try {
resolve(this.next())
} catch (error) {
reject(error)
}
})
}
}
}
module.exports = Eat