Skip to content

Commit

Permalink
Refactor plugin (#225)
Browse files Browse the repository at this point in the history
* remove comment because it is an artifact

* rename import of EventEmitter

* rename promise to maybePromiseLike

* restructure

* decouple timeout param from parent

* reduce coupling between plugin and avvio

* named import of inherits

* make loadPlugin a method of boot
decouple plugin from boot

* add AVV_ERR_READY_TIMEOUT

* move debug calls to first position of every Plugin method

* privatize load plugin method

* add TODO comment

* fix double underscore
  • Loading branch information
Uzlopak authored Jul 1, 2023
1 parent 21a2290 commit a5b5ba9
Show file tree
Hide file tree
Showing 8 changed files with 376 additions and 304 deletions.
82 changes: 75 additions & 7 deletions boot.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,10 @@ const {
kThenifyDoNotWrap
} = require('./lib/symbols')
const { TimeTree } = require('./lib/time-tree')
const { Plugin } = require('./plugin')
const { Plugin } = require('./lib/plugin')
const { debug } = require('./lib/debug')
const { validatePlugin } = require('./lib/validate-plugin')
const { isBundledOrTypescriptPlugin } = require('./lib/is-bundled-or-typescript-plugin')
const { loadPlugin } = require('./lib/load-plugin')
const { isPromiseLike } = require('./lib/is-promise-like')

function wrap (server, opts, instance) {
Expand Down Expand Up @@ -120,7 +119,11 @@ function Boot (server, opts, done) {

this._timeout = Number(opts.timeout) || 0
this._server = server
/**
* @type {Array<Plugin>}
*/
this._current = []

this._error = null
this._isOnCloseHandlerKey = kIsOnCloseHandler
this._lastUsed = null
Expand Down Expand Up @@ -152,15 +155,15 @@ function Boot (server, opts, done) {
}

this._doStart = null
this._root = new Plugin(this, root.bind(this), opts, false, 0)
this._root = new Plugin(fastq(this, this._loadPluginNextTick, 1), root.bind(this), opts, false, 0)
this._root.once('start', (serverName, funcName, time) => {
const nodeId = this.pluginTree.start(null, funcName, time)
this._root.once('loaded', (serverName, funcName, time) => {
this.pluginTree.stop(nodeId, time)
})
})

loadPlugin(this, this._root, (err) => {
this._loadPlugin(this._root, (err) => {
debug('root plugin ready')
try {
this.emit('preReady')
Expand Down Expand Up @@ -216,9 +219,9 @@ Boot.prototype._loadRegistered = function () {
const weNeedToStart = !this.started && !this.booted

// if the root plugin is not loaded, let's resume that
// so one can use after() befor calling ready
// so one can use after() before calling ready
if (weNeedToStart) {
process.nextTick(() => this._root.q.resume())
process.nextTick(() => this._root.queue.resume())
}

if (!plugin) {
Expand All @@ -244,7 +247,7 @@ Boot.prototype._addPlugin = function (plugin, opts, isAfter) {
// we always add plugins to load at the current element
const current = this._current[0]

const obj = new Plugin(this, plugin, opts, isAfter)
const obj = new Plugin(fastq(this, this._loadPluginNextTick, 1), plugin, opts, isAfter, this._timeout)
obj.once('start', (serverName, funcName, time) => {
const nodeId = this.pluginTree.start(current.name, funcName, time)
obj.once('loaded', (serverName, funcName, time) => {
Expand Down Expand Up @@ -367,6 +370,71 @@ Boot.prototype.toJSON = function () {
return this.pluginTree.toJSON()
}

/**
* @callback LoadPluginCallback
* @param {Error} [err]
*/

/**
* Load a plugin
*
* @param {Plugin} plugin
* @param {LoadPluginCallback} callback
*/
Boot.prototype._loadPlugin = function (plugin, callback) {
const instance = this
if (isPromiseLike(plugin.func)) {
plugin.func.then((fn) => {
if (typeof fn.default === 'function') {
fn = fn.default
}
plugin.func = fn
this._loadPlugin(plugin, callback)
}, callback)
return
}

const last = instance._current[0]

// place the plugin at the top of _current
instance._current.unshift(plugin)

if (instance._error && !plugin.isAfter) {
debug('skipping loading of plugin as instance errored and it is not an after', plugin.name)
process.nextTick(execCallback)
return
}

let server = (last && last.server) || instance._server

if (!plugin.isAfter) {
// Skip override for after
try {
server = instance.override(server, plugin.func, plugin.options)
} catch (overrideErr) {
debug('override errored', plugin.name)
return execCallback(overrideErr)
}
}

plugin.exec(server, execCallback)

function execCallback (err) {
plugin.finish(err, (err) => {
instance._current.shift()
callback(err)
})
}
}

/**
* Delays plugin loading until the next tick to ensure any bound `_after` callbacks have a chance
* to run prior to executing the next plugin
*/
Boot.prototype._loadPluginNextTick = function (plugin, callback) {
process.nextTick(this._loadPlugin.bind(this), plugin, callback)
}

function noop () { }

function thenify () {
Expand Down
4 changes: 4 additions & 0 deletions lib/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,9 @@ module.exports = {
AVV_ERR_READY_TIMEOUT: createError(
'AVV_ERR_READY_TIMEOUT',
"Plugin did not start in time: '%s'. You may have forgotten to call 'done' function or to resolve a Promise"
),
AVV_ERR_PLUGIN_EXEC_TIMEOUT: createError(
'AVV_ERR_PLUGIN_EXEC_TIMEOUT',
"Plugin did not start in time: '%s'. You may have forgotten to call 'done' function or to resolve a Promise"
)
}
44 changes: 0 additions & 44 deletions lib/load-plugin.js

This file was deleted.

Loading

0 comments on commit a5b5ba9

Please sign in to comment.