import { serial, flatHooks } from './utils'
|
|
export default class Hookable {
|
constructor (logger = console) {
|
this._logger = logger
|
this._hooks = {}
|
this._deprecatedHooks = {}
|
|
// Allow destructuring hook and callHook functions out of instance object
|
this.hook = this.hook.bind(this)
|
this.callHook = this.callHook.bind(this)
|
}
|
|
hook (name, fn) {
|
if (!name || typeof fn !== 'function') {
|
return
|
}
|
|
const originalName = name
|
let deprecatedHook
|
while (this._deprecatedHooks[name]) {
|
deprecatedHook = this._deprecatedHooks[name]
|
if (typeof deprecatedHook === 'string') {
|
deprecatedHook = { to: deprecatedHook }
|
}
|
name = deprecatedHook.to
|
}
|
if (deprecatedHook) {
|
if (!deprecatedHook.message) {
|
this._logger.warn(
|
`${originalName} hook has been deprecated` +
|
(deprecatedHook.to ? `, please use ${deprecatedHook.to}` : '')
|
)
|
} else {
|
this._logger.warn(deprecatedHook.message)
|
}
|
}
|
|
this._hooks[name] = this._hooks[name] || []
|
this._hooks[name].push(fn)
|
}
|
|
deprecateHook (old, name) {
|
this._deprecatedHooks[old] = name
|
}
|
|
deprecateHooks (deprecatedHooks) {
|
Object.assign(this._deprecatedHooks, deprecatedHooks)
|
}
|
|
addHooks (configHooks) {
|
const hooks = flatHooks(configHooks)
|
for (const key in hooks) {
|
this.hook(key, hooks[key])
|
}
|
}
|
|
async callHook (name, ...args) {
|
if (!this._hooks[name]) {
|
return
|
}
|
try {
|
await serial(this._hooks[name], fn => fn(...args))
|
} catch (err) {
|
if (name !== 'error') {
|
await this.callHook('error', err)
|
}
|
if (this._logger.fatal) {
|
this._logger.fatal(err)
|
} else {
|
this._logger.error(err)
|
}
|
}
|
}
|
|
clearHook (name) {
|
if (name) {
|
delete this._hooks[name]
|
}
|
}
|
|
clearHooks () {
|
this._hooks = {}
|
}
|
}
|